16 #include "libmesh/elem.h" 17 #include "libmesh/boundary_info.h" 18 #include "libmesh/edge_edge3.h" 19 #include "libmesh/fe_base.h" 20 #include "libmesh/int_range.h" 21 #include "libmesh/mesh_serializer.h" 22 #include "libmesh/mesh_triangle_holes.h" 23 #include "libmesh/poly2tri_triangulator.h" 24 #include "libmesh/unstructured_mesh.h" 31 std::unique_ptr<MeshBase>
34 const std::vector<BoundaryName> & boundary_names,
35 unsigned int num_layers,
41 const SubdomainName & output_subdomain_name)
44 std::set<std::size_t> bdry_id_set;
45 if (!boundary_names.empty())
49 bdry_id_set.insert(ids.begin(), ids.end());
51 TriangulatorInterface::MeshedHole bdry_mh(input_mesh, bdry_id_set);
52 std::vector<Point> cur_pts;
53 std::vector<Point> cur_mids;
64 std::vector<Real> layer_thicknesses(num_layers + 1);
65 const Real unit_thickness =
67 ? (thickness / num_layers)
68 : (thickness / (
std::pow(layer_bias, num_layers) - 1.0) * (layer_bias - 1.0));
69 layer_thicknesses[0] = 0.0;
70 for (
auto i :
make_range(std::vector<Real>::size_type(1), layer_thicknesses.size()))
71 layer_thicknesses[i] = unit_thickness *
std::pow(layer_bias, i - 1);
77 std::vector<std::unique_ptr<MeshBase>> polylines(num_layers + 1);
78 for (
auto layer_i :
make_range(num_layers + 1))
80 const unsigned int layer_index = outward ? layer_i : (num_layers - layer_i);
83 auto ply = std::make_unique<ReplicatedMesh>(mg.
comm());
90 std::vector<unsigned int>({1}));
91 polylines[layer_index] = std::move(ply);
93 if (layer_i + 1 < num_layers + 1)
97 auto ply_for_offset = std::make_unique<ReplicatedMesh>(mg.
comm());
104 std::vector<unsigned int>({1}));
105 std::unique_ptr<UnstructuredMesh> ply_for_offset_u =
109 &mg, ply_for_offset_u, cur_pts, cur_mids, outward, layer_thicknesses[layer_i + 1]);
110 if (cur_mids.empty())
111 cur_pts = std::move(next_combined);
114 const auto n_vert = cur_pts.size();
115 cur_pts.assign(next_combined.begin(), next_combined.begin() +
n_vert);
116 cur_mids.assign(next_combined.begin() +
n_vert, next_combined.end());
123 std::unique_ptr<MeshBase> ring;
132 if (output_subdomain_id != 0)
137 if (output_subdomain_name.size())
143 std::vector<std::unique_ptr<MeshBase>> holes;
146 holes.push_back(std::move(polylines[0]));
148 holes.push_back(std::move(ring));
151 mg, std::move(polylines[i + 1]), std::move(holes), xyd_opts);
156 std::vector<BoundaryID> bids_to_delete;
157 for (
const auto b :
make_range(2 * num_layers))
159 if (b == 1 || b == (num_layers - 1) * 2)
161 bids_to_delete.push_back(b);
163 auto & bi = ring->get_boundary_info();
164 for (
auto b : bids_to_delete)
172 std::unique_ptr<libMesh::UnstructuredMesh> & ply_mesh_u,
173 std::vector<Point> & points,
174 std::vector<Point> & mid_points,
176 const Real thickness)
181 mooseAssert(mid_points.empty(),
182 "If the input points are empty, the input mid_points must be also empty.");
184 TriangulatorInterface::MeshedHole bdry_mh(*ply_mesh_u);
197 if (mid_points.size())
198 poly2tri.
elem_type() = libMesh::ElemType::TRI6;
204 auto bdry_list(ply_mesh_u->get_boundary_info().build_side_list());
210 std::map<dof_id_type, std::vector<Point>> node_normal_map;
211 std::map<dof_id_type, Point> mid_node_normal_map;
212 for (
const auto & bside : bdry_list)
214 const auto & side = ply_mesh_u->elem_ptr(std::get<0>(bside))->side_ptr(std::get<1>(bside));
217 const Point side_normal_0 =
219 ?
getKeyNormal(ply_mesh_u->elem_ptr(std::get<0>(bside)), std::get<1>(bside), 0)
220 : ply_mesh_u->elem_ptr(std::get<0>(bside))
221 ->side_vertex_average_normal(std::get<1>(bside));
222 const Point side_normal_1 =
224 ?
getKeyNormal(ply_mesh_u->elem_ptr(std::get<0>(bside)), std::get<1>(bside), 1)
227 if (node_normal_map.count(side->node_ptr(0)->id()))
228 node_normal_map[side->node_ptr(0)->id()].push_back(side_normal_0);
230 node_normal_map[side->node_ptr(0)->id()] = {side_normal_0};
231 if (node_normal_map.count(side->node_ptr(1)->id()))
232 node_normal_map[side->node_ptr(1)->id()].push_back(side_normal_1);
234 node_normal_map[side->node_ptr(1)->id()] = {side_normal_1};
236 if (mid_points.size())
238 const Point mid_node_normal =
239 getKeyNormal(ply_mesh_u->elem_ptr(std::get<0>(bside)), std::get<1>(bside), 2);
241 [ply_mesh_u->elem_ptr(std::get<0>(bside))->node_ptr(std::get<1>(bside) + 3)->id()] =
246 std::vector<Point> mod_reduced_pts_list(points);
247 for (
const auto & [node_id, normal_vecs] : node_normal_map)
249 mooseAssert(normal_vecs.size() == 2,
250 "Each vertex should be connected to exactly two sides in a polygon.");
252 const Point original_pt = *(ply_mesh_u->node_ptr(node_id));
254 const Point move_dir =
255 (normal_vecs.front() + normal_vecs.back()).unit() * (outward ? 1.0 : -1.0);
269 const Real mov_dist =
270 thickness /
std::sqrt((1.0 + (normal_vecs.front() * normal_vecs.back()) /
271 (normal_vecs.front().norm() * normal_vecs.back().norm())) /
273 mooseAssert(std::count(points.begin(), points.end(), original_pt) == 1,
274 "The original point should be found exactly once in the reduced points list.");
275 mod_reduced_pts_list[std::distance(points.begin(),
276 std::find(points.begin(), points.end(), original_pt))] =
277 original_pt + move_dir * mov_dist;
283 for (
const auto & i_node_1 :
make_range(mod_reduced_pts_list.size()))
285 const Point & p1 = points[i_node_1];
286 const Point & p2 = mod_reduced_pts_list[i_node_1];
287 for (
const auto & i_node_2 :
make_range(mod_reduced_pts_list.size()))
289 if (i_node_2 == i_node_1 || (i_node_2 + 1) % mod_reduced_pts_list.size() == i_node_1)
291 const Point & p3 = mod_reduced_pts_list[i_node_2];
292 const Point & p4 = mod_reduced_pts_list[(i_node_2 + 1) % mod_reduced_pts_list.size()];
297 "The thickness is so large that the mesh is tangled because the offset nodes " 298 "are no longer in the same order when following the original boundary. Please " 299 "reduce the thickness value.");
303 std::vector<Point> mid_mod_reduced_pts_list(mid_points.size());
304 for (
const auto & [node_id, normal_vec] : mid_node_normal_map)
306 const Point original_pt = *(ply_mesh_u->node_ptr(node_id));
307 const Point move_dir = normal_vec.
unit() * (outward ? 1.0 : -1.0);
308 mid_mod_reduced_pts_list[std::distance(
309 mid_points.begin(),
std::find(mid_points.begin(), mid_points.end(), original_pt))] =
310 original_pt + move_dir * thickness;
315 std::vector<Point> layer_pts_list(mod_reduced_pts_list);
316 layer_pts_list.insert(
317 layer_pts_list.end(), mid_mod_reduced_pts_list.begin(), mid_mod_reduced_pts_list.end());
319 return layer_pts_list;
324 std::vector<Point> & points,
325 std::vector<Point> & mid_points,
326 const bool skip_node_reduction)
335 points.push_back(bdry_mh.
point(i));
336 if (bdry_mh.
n_midpoints() == 1 && skip_node_reduction)
337 mid_points.push_back(bdry_mh.
midpoint(0, i));
346 mooseAssert(face->type() == ElemType::EDGE3,
347 "Only elements with EDGE3 sides are supported in this function.");
348 mooseAssert(node_index < 3,
349 "The node index for an EDGE3 side should be 0, 1, or 2 (for the two vertices and the " 351 std::unique_ptr<libMesh::FEBase> fe(
353 const std::vector<Point> & normals = fe->get_normals();
354 std::vector<Point> ref_pts = {face->reference_elem()->point(node_index)};
355 fe->reinit(elem, s,
TOLERANCE, &ref_pts);
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.
std::unique_ptr< FEGenericBase< Real > > build(const unsigned int dim, const FEType &fet)
Bundle of inputs for triangulateWithDelaunay.
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
Find a value in an array.
virtual unsigned int n_points() const override
virtual void set_refine_boundary_allowed(bool refine_bdy_allowed) 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 ...
std::string tri_elem_type
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
bool has_output_subdomain_id
static constexpr Real TOLERANCE
void set_verify_hole_boundaries(bool v)
bool has_output_subdomain_name
const Parallel::Communicator & comm() const
void set_interpolate_boundary_points(int n_points)
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.
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
SubdomainID output_subdomain_id
void collectExteriorVertexPointsFromMesh(libMesh::TriangulatorInterface::MeshedHole &bdry_mh, std::vector< Point > &points, std::vector< Point > &mid_points, const bool skip_node_reduction)
TriangulationType & triangulation_type()
TypeVector< Real > unit() const
std::vector< bool > refine_holes
bool segmentsIntersect(const Point &p1, const Point &p2, const Point &p3, const Point &p4)
Check if the line segment p1-p2 intersects with line segment p3-p4 (only working in 2D (x-y plane))...
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.
std::vector< Point > generateOffsetPolyline(MeshGenerator *mg, std::unique_ptr< libMesh::UnstructuredMesh > &ply_mesh_u, std::vector< Point > &points, std::vector< Point > &mid_points, const bool outward, const Real thickness)
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 mes...
virtual unsigned int n_midpoints() const override
virtual void triangulate() override
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual Point point(const unsigned int n) const override
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sqrt(_arg)) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tanh
SubdomainName output_subdomain_name
std::vector< bool > stitch_holes
IntRange< T > make_range(T beg, T end)
Point getKeyNormal(const Elem *elem, const unsigned int s, const unsigned int node_index)
bool arePointsColinear(const Point &p1, const Point &p2, const Point &p3)
Check if three points are colinear.
virtual Point midpoint(const unsigned int m, const unsigned int n) const override
virtual Order default_order() const=0
std::unique_ptr< MeshBase > buildBoundaryLayerRing(MeshGenerator &mg, MeshBase &input_mesh, const std::vector< BoundaryName > &boundary_names, unsigned int num_layers, Real thickness, Real layer_bias, bool outward, const MooseEnum &tri_elem_type, SubdomainID output_subdomain_id, const SubdomainName &output_subdomain_name)
MooseUnits pow(const MooseUnits &, int)
const unsigned int n_vert
MeshGenerators are objects that can modify or add to an existing mesh.
bool & smooth_after_generating()