https://mooseframework.inl.gov
Functions
BoundaryLayerUtils Namespace Reference

Functions

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)
 Builds a conformal boundary-layer ring of triangulated annuli along a boundary of an input 2D mesh (or a 1D loop). More...
 
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)
 Generates a list of points offset from the input boundary polyline by a specified thickness in either outward/inward direction. More...
 
void collectExteriorVertexPointsFromMesh (libMesh::TriangulatorInterface::MeshedHole &bdry_mh, std::vector< Point > &points, std::vector< Point > &mid_points, const bool skip_node_reduction=false)
 Collects key vertex points (and optional midpoints) from a meshed hole, optionally discarding colinear vertices. More...
 
Point getKeyNormal (const Elem *elem, const unsigned int s, const unsigned int node_index)
 Extracts the normal vector of the EDGE3 side of a quadratic element at a given node index. More...
 
std::unique_ptr< MeshBasebuildBoundaryLayerRing (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)
 
std::vector< PointgenerateOffsetPolyline (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)
 
void collectExteriorVertexPointsFromMesh (libMesh::TriangulatorInterface::MeshedHole &bdry_mh, std::vector< Point > &points, std::vector< Point > &mid_points, const bool skip_node_reduction)
 
Point getKeyNormal (const Elem *elem, const unsigned int s, const unsigned int node_index)
 

Function Documentation

◆ buildBoundaryLayerRing() [1/2]

std::unique_ptr<MeshBase> BoundaryLayerUtils::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 
)

Definition at line 32 of file BoundaryLayerUtils.C.

42 {
43  // Extract seed boundary polyline points (vertices + optional midpoints) from input_mesh.
44  std::set<std::size_t> bdry_id_set;
45  if (!boundary_names.empty())
46  {
47  auto ids =
48  MooseMeshUtils::getBoundaryIDs(input_mesh, boundary_names, /*generate unknown*/ false);
49  bdry_id_set.insert(ids.begin(), ids.end());
50  }
51  TriangulatorInterface::MeshedHole bdry_mh(input_mesh, bdry_id_set);
52  std::vector<Point> cur_pts;
53  std::vector<Point> cur_mids;
54  // Preserve every input boundary node (including colinear interior nodes on straight edges) so
55  // the ring's innermost polyline can be stitched back to the input mesh's boundary exactly when
56  // keep_input is requested by a downstream caller.
58  cur_pts,
59  cur_mids,
60  /*skip_node_reduction=*/true);
61 
62  // Geometric progression of incremental thicknesses. layer_thicknesses[i] is the offset
63  // distance from polyline i-1 to polyline i (for i >= 1); layer_thicknesses[0] is unused.
64  std::vector<Real> layer_thicknesses(num_layers + 1);
65  const Real unit_thickness =
66  (layer_bias == 1.0)
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);
72 
73  // Generate the N+1 polyline meshes. Polylines are indexed so that polyline 0 is geometrically
74  // innermost (smallest) and polyline N is outermost (largest). Iteration order depends on
75  // direction: for outward, build polyline 0 first (= input boundary); for inward, build polyline
76  // N first.
77  std::vector<std::unique_ptr<MeshBase>> polylines(num_layers + 1);
78  for (auto layer_i : make_range(num_layers + 1))
79  {
80  const unsigned int layer_index = outward ? layer_i : (num_layers - layer_i);
81 
82  // Build the polyline mesh for storage as polylines[layer_index].
83  auto ply = std::make_unique<ReplicatedMesh>(mg.comm());
85  cur_pts,
86  cur_mids,
87  /*loop=*/true,
88  BoundaryName(),
89  BoundaryName(),
90  std::vector<unsigned int>({1}));
91  polylines[layer_index] = std::move(ply);
92 
93  if (layer_i + 1 < num_layers + 1)
94  {
95  // Build a sacrificial polyline mesh to feed generateOffsetPolyline (it triangulates the
96  // input mesh in place to compute side normals; we discard it afterwards).
97  auto ply_for_offset = std::make_unique<ReplicatedMesh>(mg.comm());
98  MooseMeshUtils::buildPolyLineMesh(*ply_for_offset,
99  cur_pts,
100  cur_mids,
101  /*loop=*/true,
102  BoundaryName(),
103  BoundaryName(),
104  std::vector<unsigned int>({1}));
105  std::unique_ptr<UnstructuredMesh> ply_for_offset_u =
106  dynamic_pointer_cast<UnstructuredMesh>(std::move(ply_for_offset));
107 
108  std::vector<Point> next_combined = generateOffsetPolyline(
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);
112  else
113  {
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());
117  }
118  }
119  }
120 
121  // Triangulate each annulus (between polylines[i] and polylines[i+1]). Stitch each annulus to
122  // the accumulating ring (except the first one).
123  std::unique_ptr<MeshBase> ring;
124  for (auto i : make_range(num_layers))
125  {
127  xyd_opts.refine_bdy = false;
128  xyd_opts.verify_holes = false;
129  xyd_opts.stitch_holes = {i > 0};
130  xyd_opts.refine_holes = {false};
131  xyd_opts.tri_elem_type = std::string(tri_elem_type);
132  if (output_subdomain_id != 0)
133  {
134  xyd_opts.has_output_subdomain_id = true;
135  xyd_opts.output_subdomain_id = output_subdomain_id;
136  }
137  if (output_subdomain_name.size())
138  {
139  xyd_opts.has_output_subdomain_name = true;
140  xyd_opts.output_subdomain_name = output_subdomain_name;
141  }
142 
143  std::vector<std::unique_ptr<MeshBase>> holes;
144  holes.reserve(1);
145  if (i == 0)
146  holes.push_back(std::move(polylines[0]));
147  else
148  holes.push_back(std::move(ring));
149 
151  mg, std::move(polylines[i + 1]), std::move(holes), xyd_opts);
152  }
153  // We now have 2 * num_layers boundaries
154  // Let's only keep the innermost (1) and outermost (2 * num_layers) boundaries, and remove all
155  // intermediate ring bcids
156  std::vector<BoundaryID> bids_to_delete;
157  for (const auto b : make_range(2 * num_layers))
158  {
159  if (b == 1 || b == (num_layers - 1) * 2)
160  continue;
161  bids_to_delete.push_back(b);
162  }
163  auto & bi = ring->get_boundary_info();
164  for (auto b : bids_to_delete)
165  bi.remove_id(b);
166 
167  return ring;
168 }
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.
Bundle of inputs for triangulateWithDelaunay.
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.
void collectExteriorVertexPointsFromMesh(libMesh::TriangulatorInterface::MeshedHole &bdry_mh, std::vector< Point > &points, std::vector< Point > &mid_points, const bool skip_node_reduction)
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...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)
MooseUnits pow(const MooseUnits &, int)
Definition: Units.C:537
const unsigned int n_vert

◆ buildBoundaryLayerRing() [2/2]

std::unique_ptr<MeshBase> BoundaryLayerUtils::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 
)

Builds a conformal boundary-layer ring of triangulated annuli along a boundary of an input 2D mesh (or a 1D loop).

Generates num_layers + 1 parallel polylines (geometric progression of thicknesses with the given bias), triangulates each annulus with triangulateWithDelaunay, and sequentially stitches them. The output mesh carries 2 * num_layers boundary ids, with the innermost being id 1 and the outermost being id (num_layers - 1) * 2.

Parameters
mgThe calling mesh generator (for paramError reporting + buildMeshBaseObject)
input_meshThe 2D-XY input mesh or 1D closed loop providing the seed boundary
boundary_namesSubset of boundary names on input_mesh defining the seed boundary; if empty, the external boundary of input_mesh is auto-detected via MeshedHole
num_layersNumber of element layers to generate
thicknessTotal boundary-layer thickness
layer_biasGeometric growth factor between successive layer thicknesses (1.0 = uniform)
outwardIf true, the layer grows outward from the seed boundary; else inward
tri_elem_typeTriangle element type ("TRI3", "TRI6", "TRI7", or "DEFAULT")
output_subdomain_idSubdomain id assigned to all generated triangles (0 = default)
output_subdomain_nameSubdomain name assigned to output_subdomain_id (empty = unnamed)
Returns
The stitched boundary-layer ring mesh

Referenced by XYTriangleBoundaryLayerGenerator::generate(), and XYDelaunayGenerator::generate().

◆ collectExteriorVertexPointsFromMesh() [1/2]

void BoundaryLayerUtils::collectExteriorVertexPointsFromMesh ( libMesh::TriangulatorInterface::MeshedHole bdry_mh,
std::vector< Point > &  points,
std::vector< Point > &  mid_points,
const bool  skip_node_reduction = false 
)

Collects key vertex points (and optional midpoints) from a meshed hole, optionally discarding colinear vertices.

Parameters
bdry_mhThe 2D MeshedHole object from which to collect key points
pointsThe vector to which collected vertex points are appended (in order)
mid_pointsThe vector to which collected midpoints are appended; only populated when the MeshedHole reports a midpoint per side AND skip_node_reduction is true
skip_node_reductionIf true, retain every vertex and midpoint; if false, drop vertices that are colinear with their two neighbors and do not collect midpoints

◆ collectExteriorVertexPointsFromMesh() [2/2]

void BoundaryLayerUtils::collectExteriorVertexPointsFromMesh ( libMesh::TriangulatorInterface::MeshedHole bdry_mh,
std::vector< Point > &  points,
std::vector< Point > &  mid_points,
const bool  skip_node_reduction 
)

Definition at line 323 of file BoundaryLayerUtils.C.

Referenced by buildBoundaryLayerRing(), and generateOffsetPolyline().

327 {
328  for (const auto i : make_range(bdry_mh.n_points()))
329  {
330  if (skip_node_reduction || !geom_utils::arePointsColinear(
331  bdry_mh.point((i - 1 + bdry_mh.n_points()) % bdry_mh.n_points()),
332  bdry_mh.point(i),
333  bdry_mh.point((i + 1) % bdry_mh.n_points())))
334  {
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));
338  }
339  }
340 }
virtual unsigned int n_points() const override
virtual unsigned int n_midpoints() const override
virtual Point point(const unsigned int n) const override
IntRange< T > make_range(T beg, T end)
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

◆ generateOffsetPolyline() [1/2]

std::vector<Point> BoundaryLayerUtils::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 
)

Generates a list of points offset from the input boundary polyline by a specified thickness in either outward/inward direction.

Parameters
mgThe mesh generator calling this function, used for paramError reporting
ply_mesh_uThe 1D loop polyline mesh of the original boundary; the volume it encloses will be triangulated in-place to compute the normal and define the inward/outward directions
pointsThe vertex points of the original polyline. If empty, populated from the input mesh via collectExteriorVertexPointsFromMesh.
mid_pointsThe midpoints of the original polyline (optional to enable quadratic elements). If both this and points are empty, populated from the input mesh.
outwardWhether to offset in the outward (true) or inward (false) direction
thicknessThe offset distance (>= 0)
Returns
Offset points: vertices first, then midpoints (length = points.size() + mid_points.size())

◆ generateOffsetPolyline() [2/2]

std::vector<Point> BoundaryLayerUtils::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 
)

Definition at line 171 of file BoundaryLayerUtils.C.

Referenced by buildBoundaryLayerRing().

177 {
178  // If the input points are empty, we will extract them from the input 1D mesh
179  if (points.empty())
180  {
181  mooseAssert(mid_points.empty(),
182  "If the input points are empty, the input mid_points must be also empty.");
183 
184  TriangulatorInterface::MeshedHole bdry_mh(*ply_mesh_u);
185  collectExteriorVertexPointsFromMesh(bdry_mh, points, mid_points);
186  }
187  // Generate a very simple triangulation mesh so that we can get the outward normal vectors
188  libMesh::Poly2TriTriangulator poly2tri(*ply_mesh_u);
189  poly2tri.triangulation_type() = libMesh::TriangulatorInterface::PSLG;
190 
191  poly2tri.set_interpolate_boundary_points(0);
192  poly2tri.set_refine_boundary_allowed(false);
193  poly2tri.set_verify_hole_boundaries(false);
194  poly2tri.desired_area() = 0;
195  poly2tri.minimum_angle() = 0; // Not yet supported
196  poly2tri.smooth_after_generating() = false;
197  if (mid_points.size())
198  poly2tri.elem_type() = libMesh::ElemType::TRI6;
199  poly2tri.triangulate();
200 
201  // We need to serialize the mesh for next steps
202  libMesh::MeshSerializer serial(*ply_mesh_u);
203  // The mesh now only contains one side set that corresponds to the outer boundary with an ID of 0
204  auto bdry_list(ply_mesh_u->get_boundary_info().build_side_list());
205 
206  // For each vertex, the shifting direction to form the offset is defined by the normal vectors of
207  // the two sides that contain the vertex.
208  // We gather the normals for each node on the boundary here, which are all vertices because of
209  // the pre-selection of nodes.
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)
213  {
214  const auto & side = ply_mesh_u->elem_ptr(std::get<0>(bside))->side_ptr(std::get<1>(bside));
215  // For linear elements, the side normal is constant and can be obtained straightforwardly;
216  // For quadratic elements, we need the normal at the shared vertices
217  const Point side_normal_0 =
218  mid_points.size()
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 =
223  mid_points.size()
224  ? getKeyNormal(ply_mesh_u->elem_ptr(std::get<0>(bside)), std::get<1>(bside), 1)
225  : side_normal_0;
226 
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);
229  else
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);
233  else
234  node_normal_map[side->node_ptr(1)->id()] = {side_normal_1};
235 
236  if (mid_points.size())
237  {
238  const Point mid_node_normal =
239  getKeyNormal(ply_mesh_u->elem_ptr(std::get<0>(bside)), std::get<1>(bside), 2);
240  mid_node_normal_map
241  [ply_mesh_u->elem_ptr(std::get<0>(bside))->node_ptr(std::get<1>(bside) + 3)->id()] =
242  mid_node_normal;
243  }
244  }
245 
246  std::vector<Point> mod_reduced_pts_list(points);
247  for (const auto & [node_id, normal_vecs] : node_normal_map)
248  {
249  mooseAssert(normal_vecs.size() == 2,
250  "Each vertex should be connected to exactly two sides in a polygon.");
251 
252  const Point original_pt = *(ply_mesh_u->node_ptr(node_id));
253  // Form an average normal at the vertex from the two connected sides' normals
254  const Point move_dir =
255  (normal_vecs.front() + normal_vecs.back()).unit() * (outward ? 1.0 : -1.0);
256  // Consider four points of interest to determine the moving distance
257  // 1. the vertex point
258  // 2. point along normal_vecs.front() from the vertex with a distance thickness
259  // 3. point along normal_vecs.back() from the vertex with a distance thickness
260  // 4. point along move_dir from the vertex with a distance mov_dist
261  // The four points form a kite shape with its symmetry axis along 1-4 direction
262  // Angle 1-2-4 and angle 1-3-4 are right angles
263  // Angle 2-1-3 (i.e., theta) can be calculated using the interior product of
264  // v1 = normal_vecs.front() and v2 = normal_vecs.back():
265  // cos(theta) = (v1 . v2) / (|v1| * |v2|)
266  // Because of the right angles, the distance between 1 and 4 can be calculated as:
267  // mov_dist = thickness / cos(theta/2)
268  // and cos(theta/2) = sqrt((1 + cos(theta)) / 2)
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())) /
272  2.0);
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;
278  }
279 
280  // To ensure no overlapping, we need to check set of four points
281  // p1 and p2 should be a pair of points before and after shifting
282  // p3 and p4 should be a pair of adjacent shifted points that are neither not p2
283  for (const auto & i_node_1 : make_range(mod_reduced_pts_list.size()))
284  {
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()))
288  {
289  if (i_node_2 == i_node_1 || (i_node_2 + 1) % mod_reduced_pts_list.size() == i_node_1)
290  continue;
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()];
293  if (thickness > 0)
294  if (geom_utils::segmentsIntersect(p1, p2, p3, p4))
295  mg->paramError(
296  "thickness",
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.");
300  }
301  }
302 
303  std::vector<Point> mid_mod_reduced_pts_list(mid_points.size());
304  for (const auto & [node_id, normal_vec] : mid_node_normal_map)
305  {
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;
311  }
312 
313  // combine mod_reduced_pts_list and mid_mod_reduced_pts_list to get the final list of points for
314  // the layer mesh
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());
318 
319  return layer_pts_list;
320 }
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
Find a value in an array.
Definition: KokkosUtils.h:40
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:467
void collectExteriorVertexPointsFromMesh(libMesh::TriangulatorInterface::MeshedHole &bdry_mh, std::vector< Point > &points, std::vector< Point > &mid_points, const bool skip_node_reduction)
TypeVector< Real > unit() const
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))...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sqrt(_arg)) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tanh
IntRange< T > make_range(T beg, T end)
Point getKeyNormal(const Elem *elem, const unsigned int s, const unsigned int node_index)
const Elem & get(const ElemType type_in)

◆ getKeyNormal() [1/2]

Point BoundaryLayerUtils::getKeyNormal ( const Elem *  elem,
const unsigned int  s,
const unsigned int  node_index 
)

Extracts the normal vector of the EDGE3 side of a quadratic element at a given node index.

Parameters
elemThe element containing the side of interest
sThe side index of interest
node_indexThe index of the node on the side at which to extract the normal vector
Returns
The normal vector at the node index on the side of interest

◆ getKeyNormal() [2/2]

Point BoundaryLayerUtils::getKeyNormal ( const Elem elem,
const unsigned int  s,
const unsigned int  node_index 
)

Definition at line 343 of file BoundaryLayerUtils.C.

Referenced by generateOffsetPolyline().

344 {
345  const std::unique_ptr<const Elem> face = elem->build_side_ptr(s);
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 "
350  "midpoint).");
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);
356  return normals[0];
357 }
std::unique_ptr< FEGenericBase< Real > > build(const unsigned int dim, const FEType &fet)
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
virtual Order default_order() const=0