https://mooseframework.inl.gov
Loading...
Searching...
No Matches
CircularBoundaryCorrectionGenerator.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
11
12#include "MooseUtils.h"
13#include "LinearInterpolation.h"
15#include "CastUniquePointer.h"
16
17// C++ includes
18#include <cmath> // for atan2
19
21
24{
26
27 params.addRequiredParam<MeshGeneratorName>("input", "The input mesh to be modified.");
28
29 params.addParam<bool>("move_end_nodes_in_span_direction",
30 false,
31 "Whether to move the end nodes in the span direction of the arc or not.");
32
33 params.addRequiredParam<std::vector<BoundaryName>>("input_mesh_circular_boundaries",
34 "Names of the circular boundaries.");
35
36 params.addRangeCheckedParam<std::vector<Real>>(
37 "transition_layer_ratios",
38 "transition_layer_ratios>0&transition_layer_ratios<=1.0",
39 "Ratios to radii as the transition layers on both sides of the original radii (if this "
40 "parameter is not provided, 0.01 will be used).");
41
42 params.addRangeCheckedParam<std::vector<Real>>(
43 "custom_circular_tolerance",
44 "custom_circular_tolerance>0",
45 "Custom tolerances (L2 norm) used to verify whether the provided boundaries are circular "
46 "(default is 1.0e-6).");
47
49 "This CircularBoundaryCorrectionGenerator object is designed to correct full "
50 "or partial circular boundaries in a 2D mesh to preserve areas.");
51
52 return params;
53}
54
56 const InputParameters & parameters)
57 : MeshGenerator(parameters),
58 _input_name(getParam<MeshGeneratorName>("input")),
59 _input_mesh_circular_boundaries(
60 getParam<std::vector<BoundaryName>>("input_mesh_circular_boundaries")),
61 _transition_layer_ratios(isParamValid("transition_layer_ratios")
62 ? getParam<std::vector<Real>>("transition_layer_ratios")
63 : std::vector<Real>(_input_mesh_circular_boundaries.size(), 0.01)),
64 _custom_circular_tolerance(
65 isParamValid("custom_circular_tolerance")
66 ? (getParam<std::vector<Real>>("custom_circular_tolerance").size() == 1
67 ? std::vector<Real>(
68 _input_mesh_circular_boundaries.size(),
69 getParam<std::vector<Real>>("custom_circular_tolerance").front())
70 : getParam<std::vector<Real>>("custom_circular_tolerance"))
71 : std::vector<Real>(_input_mesh_circular_boundaries.size(), 1.0e-6)),
72 _move_end_nodes_in_span_direction(getParam<bool>("move_end_nodes_in_span_direction")),
73 _input(getMeshByName(_input_name))
74{
76 paramError("transition_layer_ratios",
77 "this parameter must have the same length as 'input_mesh_circular_boundaries'.");
79 paramError("custom_circular_tolerance",
80 "if provided, this parameter must have either a unity length or the same length as "
81 "'input_mesh_circular_boundaries'.");
82}
83
84std::unique_ptr<MeshBase>
86{
87 auto input_mesh = dynamic_cast<ReplicatedMesh *>(_input.get());
88 if (!input_mesh)
89 paramError("input", "Input is not a replicated mesh, which is required");
90
91 // Check if the input mesh has the given boundaries
92 for (const auto & bd : _input_mesh_circular_boundaries)
93 {
94 if (!MooseMeshUtils::hasBoundaryNameOrID(*input_mesh, bd))
95 paramError("input_mesh_circular_boundaries",
96 "the boundary '" + bd + "' does not exist in the input mesh.");
97 }
100
101 auto side_list = input_mesh->get_boundary_info().build_side_list();
102 input_mesh->get_boundary_info().build_node_list_from_side_list();
103 auto node_list = input_mesh->get_boundary_info().build_node_list();
104
105 std::vector<std::vector<Point>> input_circ_bds_pts(_input_mesh_circular_bids.size());
106 // Loop over all the boundary nodes and store the nodes (points) on each of the circular
107 // boundaries to be corrected
108 for (unsigned int i = 0; i < node_list.size(); ++i)
109 {
110 auto node_list_it = std::find(_input_mesh_circular_bids.begin(),
112 std::get<1>(node_list[i]));
113 if (node_list_it != _input_mesh_circular_bids.end())
114 {
115 input_circ_bds_pts[std::distance(_input_mesh_circular_bids.begin(), node_list_it)].push_back(
116 input_mesh->point(std::get<0>(node_list[i])));
117 if (!MooseUtils::absoluteFuzzyEqual(
118 input_circ_bds_pts[std::distance(_input_mesh_circular_bids.begin(), node_list_it)]
119 .back()(2),
120 0.0))
121 paramError("input_mesh_circular_boundaries", "Only boundaries in XY plane are supported.");
122 }
123 }
124 std::vector<std::vector<std::pair<Point, Point>>> input_circ_bds_sds(
126 std::vector<std::vector<std::pair<dof_id_type, dof_id_type>>> input_circ_bds_sds_ids(
128 // Loop over all the boundary sides and store the sides (node pairs) on each of the circular
129 // boundaries to be corrected
130 for (unsigned int i = 0; i < side_list.size(); ++i)
131 {
132 auto side_list_it = std::find(_input_mesh_circular_bids.begin(),
134 std::get<2>(side_list[i]));
135 if (side_list_it != _input_mesh_circular_bids.end())
136 {
137 const auto side =
138 input_mesh->elem_ptr(std::get<0>(side_list[i]))->side_ptr(std::get<1>(side_list[i]));
139 // In case the sideset contains a pair of duplicated sides with different orientations, we
140 // only store the first side found.
141 if (std::find(
142 input_circ_bds_sds_ids[std::distance(_input_mesh_circular_bids.begin(), side_list_it)]
143 .begin(),
144 input_circ_bds_sds_ids[std::distance(_input_mesh_circular_bids.begin(), side_list_it)]
145 .end(),
146 std::make_pair(side->node_id(0), side->node_id(1))) ==
147 input_circ_bds_sds_ids[std::distance(_input_mesh_circular_bids.begin(), side_list_it)]
148 .end() &&
149 std::find(
150 input_circ_bds_sds_ids[std::distance(_input_mesh_circular_bids.begin(), side_list_it)]
151 .begin(),
152 input_circ_bds_sds_ids[std::distance(_input_mesh_circular_bids.begin(), side_list_it)]
153 .end(),
154 std::make_pair(side->node_id(1), side->node_id(0))) ==
155 input_circ_bds_sds_ids[std::distance(_input_mesh_circular_bids.begin(), side_list_it)]
156 .end())
157 {
158 input_circ_bds_sds[std::distance(_input_mesh_circular_bids.begin(), side_list_it)]
159 .push_back(std::make_pair(side->point(0), side->point(1)));
160 input_circ_bds_sds_ids[std::distance(_input_mesh_circular_bids.begin(), side_list_it)]
161 .push_back(std::make_pair(side->node_id(0), side->node_id(1)));
162 }
163 }
164 }
165
166 // Computes the correction coefficient and apply it;
167 // Also records the nodes that have been modified to avert double-correction
168 std::set<dof_id_type> mod_node_list;
169 // And check if any of the boundaries are partial circles
170 bool has_partial_circle = false;
171 for (unsigned int i = 0; i < input_circ_bds_pts.size(); i++)
172 {
173 auto & input_circ_bd_pts = input_circ_bds_pts[i];
174 if (input_circ_bd_pts.size() < 3)
175 paramError("input_mesh_circular_boundaries",
176 "too few nodes in one of the provided boundaries.");
177 Real bdry_rad;
178 const Point boundary_origin =
179 circularCenterCalculator(input_circ_bd_pts, bdry_rad, _custom_circular_tolerance[i]);
180
181 // Check if the boundary is a full circle or partial
182 // Also make an ordered array of nodes
183 Real dummy_max_rad = 0;
184 std::vector<dof_id_type> ordered_node_list;
185 bool is_bdry_closed;
187 dummy_max_rad,
188 ordered_node_list,
189 input_circ_bds_sds_ids[i],
190 boundary_origin,
191 "boundary",
192 is_bdry_closed,
193 true);
194
195 has_partial_circle = has_partial_circle || !is_bdry_closed;
196 // If the user selects to move the end nodes of a partial circular boundary, we need to
197 // calculate the displacement of the end nodes, which differs from the displacement of the
198 // other nodes
199 Real end_node_disp;
200 // c_coeff is the coefficient used to scale the azimuthal angles
201 // corr_factor > 1 means the partial boundary is less than a half circle
202 // corr_factor < 1 means the partial boundary is more than a half circle
203 // corr_factor = 1 means the boundary is a full circle or a half circle
204 Real c_coeff;
205 const Real corr_factor = generateRadialCorrectionFactor(input_circ_bds_sds[i],
206 boundary_origin,
207 is_bdry_closed,
209 c_coeff,
210 end_node_disp);
211 // Radial range that within which the nodes will be moved
212 const Real transition_layer_thickness = _transition_layer_ratios[i] * bdry_rad;
213 // For a partial boundary, we take out the start and end nodes from the ordered node list
214 const Point pt_start = input_mesh->point(ordered_node_list.front());
215 const Point pt_end = input_mesh->point(ordered_node_list.back());
216 // The direction of the span of the partial boundary (which is an arc)
217 const Point span_direction = is_bdry_closed ? Point(0.0, 0.0, 0.0) : (pt_start - pt_end).unit();
218 // Although these are also calculated for a full circular boundary, they are not used
219
220 // Find the center of the circular boundary's azimuthal angle and the half of the azimuthal
221 // range
222 Real center_bdry_azi;
223 Real half_azi_range;
224 if (MooseUtils::absoluteFuzzyEqual(c_coeff, 1.0))
225 {
226 // For a half circle, the center boundary azimuthal angle is the starting node's azimuthal
227 // plus or minus pi/2
228 const Point pt_second = input_mesh->point(ordered_node_list[1]);
229 const Real pt_start_azi =
230 std::atan2(pt_start(1) - boundary_origin(1), pt_start(0) - boundary_origin(0));
231 const Real pt_second_azi =
232 std::atan2(pt_second(1) - boundary_origin(1), pt_second(0) - boundary_origin(0));
233 // Figure out whether it is plus or minus pi/2
234 const Real azi_direction =
235 (pt_second_azi - pt_start_azi > 0 or pt_second_azi - pt_start_azi < -M_PI) ? 1.0 : -1.0;
236 center_bdry_azi = pt_start_azi + azi_direction * M_PI / 2.0;
237 half_azi_range = M_PI / 2.0;
238 }
239 else
240 {
241 const Point center_bdry =
242 (c_coeff > 1.0 ? 1.0 : -1.0) * (pt_start + pt_end) / 2.0 - boundary_origin;
243 const Real pt_start_azi =
244 std::atan2(pt_start(1) - boundary_origin(1), pt_start(0) - boundary_origin(0));
245 center_bdry_azi = std::atan2(center_bdry(1), center_bdry(0));
246 half_azi_range = std::abs(pt_start_azi - center_bdry_azi > M_PI
247 ? pt_start_azi - center_bdry_azi - 2 * M_PI
248 : (pt_start_azi - center_bdry_azi < -M_PI
249 ? pt_start_azi - center_bdry_azi + 2 * M_PI
250 : pt_start_azi - center_bdry_azi));
251 }
252 // Loop over all the nodes in the mesh to move the nodes in the transition layer so that the
253 // circular boundary is corrected
254 for (auto & node : input_mesh->node_ptr_range())
255 {
256 // If the end nodes are set to move in the span direction, we should do so
257 if (node->id() == ordered_node_list.front() && _move_end_nodes_in_span_direction &&
258 end_node_disp >= 0.0)
259 {
260 if (!mod_node_list.emplace((*node).id()).second)
261 paramError("transition_layer_ratios", "the transition layers are overlapped.");
262 (*node) = (*node) + end_node_disp * bdry_rad * span_direction;
263 continue;
264 }
265 if (node->id() == ordered_node_list.back() && _move_end_nodes_in_span_direction &&
266 end_node_disp >= 0.0)
267 {
268 if (!mod_node_list.emplace((*node).id()).second)
269 paramError("transition_layer_ratios", "the transition layers are overlapped.");
270 (*node) = (*node) - end_node_disp * bdry_rad * span_direction;
271 continue;
272 }
273 const Point tmp_pt = (*node) - boundary_origin;
274 const Real tmp_pt_azi = std::atan2(tmp_pt(1), tmp_pt(0));
275 const Real angle_diff =
276 tmp_pt_azi - center_bdry_azi > M_PI
277 ? tmp_pt_azi - center_bdry_azi - 2.0 * M_PI
278 : (tmp_pt_azi - center_bdry_azi < -M_PI ? tmp_pt_azi - center_bdry_azi + 2.0 * M_PI
279 : tmp_pt_azi - center_bdry_azi);
280 const Real pt_rad = tmp_pt.norm();
281 // Skip the node if it is not within the radial and azimuthal range
282 if ((!is_bdry_closed && MooseUtils::absoluteFuzzyGreaterThan(
283 std::abs(angle_diff), half_azi_range, libMesh::TOLERANCE)) ||
284 pt_rad < bdry_rad - transition_layer_thickness ||
285 pt_rad > bdry_rad + transition_layer_thickness)
286 continue;
287 const Real tmp_pt_azi_new =
288 (_move_end_nodes_in_span_direction ? c_coeff : 1.0) * angle_diff + center_bdry_azi;
289 const Real mod_corr_factor =
290 pt_rad <= bdry_rad
291 ? (1.0 + (corr_factor - 1.0) * (pt_rad - bdry_rad + transition_layer_thickness) /
292 transition_layer_thickness)
293 : (1.0 + (corr_factor - 1.0) * (bdry_rad + transition_layer_thickness - pt_rad) /
294 transition_layer_thickness);
295 if (!MooseUtils::absoluteFuzzyEqual(mod_corr_factor, 1.0))
296 {
297 if (!mod_node_list.emplace((*node).id()).second)
298 paramError("transition_layer_ratios", "the transition layers are overlapped.");
299 }
300 const Point tmp_pt_alt =
301 Point(pt_rad * std::cos(tmp_pt_azi_new), pt_rad * std::sin(tmp_pt_azi_new), tmp_pt(2)) *
302 mod_corr_factor;
303 (*node) = tmp_pt_alt + boundary_origin;
304 }
305 }
306 if (!has_partial_circle && _move_end_nodes_in_span_direction)
307 paramError("move_end_nodes_in_span_direction",
308 "all the boundaries are closed, this parameter should be be set as 'true'.");
309 // Loop over all the elements to check if any of them are inverted due to the correction
310 for (auto & elem : input_mesh->element_ptr_range())
311 if (elem->volume() < 0.0)
312 paramError("transition_layer_ratios",
313 "some elements are inverted during circular boundary correction, consider tuning "
314 "this parameter.");
315
316 return dynamic_pointer_cast<MeshBase>(_input);
317}
318
319Point
321 Real & radius,
322 const Real tol) const
323{
324 // Usually, just using the first three points would work
325 // Here a more complex selection is made in case the first three points are too close to each
326 // other.
327 // Step 1: find the farthest point from the first point
328 Real tmp_dis(0.0);
329 unsigned int farthest_pt_id(0);
330 for (unsigned int i = 1; i < pts_list.size(); i++)
331 if ((pts_list[i] - pts_list[0]).norm() > tmp_dis)
332 {
333 tmp_dis = (pts_list[i] - pts_list[0]).norm();
334 farthest_pt_id = i;
335 }
336 // Step 2: find the third point that is nearly in the middle of the first two
337 unsigned int mid_pt_id(0);
338 for (unsigned int i = 1; i < pts_list.size(); i++)
339 if (i != farthest_pt_id && std::abs((pts_list[farthest_pt_id] - pts_list[i]).norm() -
340 (pts_list[0] - pts_list[i]).norm()) < tmp_dis)
341 {
342 tmp_dis = std::abs((pts_list[farthest_pt_id] - pts_list[i]).norm() -
343 (pts_list[0] - pts_list[i]).norm());
344 mid_pt_id = i;
345 }
346
347 const Real x12 = pts_list[0](0) - pts_list[farthest_pt_id](0);
348 const Real x13 = pts_list[0](0) - pts_list[mid_pt_id](0);
349
350 const Real y12 = pts_list[0](1) - pts_list[farthest_pt_id](1);
351 const Real y13 = pts_list[0](1) - pts_list[mid_pt_id](1);
352
353 const Real sx13 =
354 pts_list[0](0) * pts_list[0](0) - pts_list[mid_pt_id](0) * pts_list[mid_pt_id](0);
355 const Real sy13 =
356 pts_list[0](1) * pts_list[0](1) - pts_list[mid_pt_id](1) * pts_list[mid_pt_id](1);
357
358 const Real sx21 =
359 pts_list[farthest_pt_id](0) * pts_list[farthest_pt_id](0) - pts_list[0](0) * pts_list[0](0);
360 const Real sy21 =
361 pts_list[farthest_pt_id](1) * pts_list[farthest_pt_id](1) - pts_list[0](1) * pts_list[0](1);
362
363 const Real f =
364 (sx13 * x12 + sy13 * x12 + sx21 * x13 + sy21 * x13) / (2.0 * (-y13 * x12 + y12 * x13));
365 const Real g =
366 (sx13 * y12 + sy13 * y12 + sx21 * y13 + sy21 * y13) / (2.0 * (-x13 * y12 + x12 * y13));
367
368 const Real c = -pts_list[0](0) * pts_list[0](0) - pts_list[0](1) * pts_list[0](1) -
369 2.0 * g * pts_list[0](0) - 2.0 * f * pts_list[0](1);
370 const Real r2 = f * f + g * g - c;
371
372 radius = std::sqrt(r2);
373 const Point circ_origin = Point(-g, -f, 0.0);
374
375 for (const auto & pt : pts_list)
376 if (!MooseUtils::absoluteFuzzyEqual((pt - circ_origin).norm(), radius, tol))
377 paramError("input_mesh_circular_boundaries",
378 "the boundary provided is not circular. The generator found point " +
379 Moose::stringify(circ_origin) +
380 " to be the likely circle origin to the boundary, with a radius of " +
381 std::to_string(radius) + ". However, boundary node " + Moose::stringify(pt) +
382 " is at a radius of " + std::to_string((pt - circ_origin).norm()));
383 return circ_origin;
384}
385
386Real
388 const std::vector<std::pair<Point, Point>> & bd_side_list,
389 const Point & circle_center,
390 const bool is_closed_loop,
391 const bool move_end_nodes_in_span_direction,
392 Real & c_coeff,
393 Real & end_node_disp) const
394{
395 if (bd_side_list.empty())
396 mooseError("The 'bd_side_list' argument of "
397 "CircularBoundaryCorrectionGenerator::generateRadialCorrectionFactor is empty.");
398 Real acc_area(0.0);
399 Real acc_azi(0.0);
400 std::vector<Real> d_azi_list;
401 for (const auto & bd_side : bd_side_list)
402 {
403 const Point v1 = bd_side.first - circle_center;
404 const Point v2 = bd_side.second - circle_center;
405 // Use cross to calculate r * r * sin(d_azi_i) and then normalized by r * r to get sin(d_azi_i)
406 acc_area += v1.cross(v2).norm() / v1.norm() / v2.norm();
407 const Real azi1 = std::atan2(v1(1), v1(0));
408 const Real azi2 = std::atan2(v2(1), v2(0));
409 const Real d_azi =
410 std::abs(azi1 - azi2) > M_PI ? 2 * M_PI - std::abs(azi1 - azi2) : std::abs(azi1 - azi2);
411 acc_azi += d_azi;
412 d_azi_list.push_back(d_azi);
413 }
414
415 if (!MooseUtils::absoluteFuzzyEqual(acc_azi, M_PI) && !is_closed_loop &&
416 move_end_nodes_in_span_direction)
417 {
418 const Real k_1 = 1.0 + std::cos(acc_azi);
419 const Real k_2 = acc_azi - std::sin(acc_azi);
420
421 unsigned int ct = 0;
422 Real diff = 1.0;
423 Real c_old = 1.0;
424 Real c_new = 1.0;
425 while (diff >= libMesh::TOLERANCE && ct < 100)
426 {
427 c_old = c_new;
428 const Real gc = k_2 + k_2 * std::cos(acc_azi * c_old) + k_1 * std::sin(acc_azi * c_old) -
429 k_1 * sineSummation(d_azi_list, c_old);
430 const Real gcp = -k_2 * acc_azi * std::sin(acc_azi * c_old) +
431 k_1 * acc_azi * std::cos(acc_azi * c_old) -
432 k_1 * sinePrimeSummation(d_azi_list, c_old);
433 c_new = c_old - gc / gcp;
434 diff = std::abs(c_new - c_old);
435 ct++;
436 }
437
438 if (ct >= 100)
439 mooseError("Newton-Raphson method did not converge for generating the radial correction "
440 "factor for circular area preservation.");
441
442 const Real norm_corr_factor = std::cos(0.5 * acc_azi) / std::cos(0.5 * acc_azi * c_new);
443 end_node_disp = norm_corr_factor * std::sin(0.5 * acc_azi * c_new) - std::sin(0.5 * acc_azi);
444 c_coeff = c_new;
445
446 return norm_corr_factor;
447 }
448 end_node_disp = -1.0;
449 c_coeff = 1.0;
450 return std::sqrt(acc_azi / acc_area);
451}
452
453Real
455 const Real coeff) const
456{
457 Real acc(0.0);
458 for (const auto & th : th_list)
459 acc += std::sin(th * coeff);
460 return acc;
461}
462
463Real
465 const Real coeff) const
466{
467 // Note that this calculates the derivative with regards to coeff
468 Real acc(0.0);
469 for (const auto & th : th_list)
470 acc += th * std::cos(th * coeff);
471 return acc;
472}
registerMooseObject("MooseApp", CircularBoundaryCorrectionGenerator)
This CircularBoundaryCorrectionGenerator object is designed to correct full or partial circular bound...
const bool _move_end_nodes_in_span_direction
Whether to move the end nodes of the partial circular boundary in the span direction.
std::unique_ptr< MeshBase > & _input
Reference to input mesh pointer.
const std::vector< BoundaryName > _input_mesh_circular_boundaries
Names of the circular boundaries of the 2D input mesh to correct.
const std::vector< Real > _transition_layer_ratios
Ratio parameters used to customize the transition area sizes.
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
Point circularCenterCalculator(const std::vector< Point > &pts_list, Real &radius, const Real tol=1e-12) const
Calculates the center of the circle/partial circle formed by a vector of points.
Real generateRadialCorrectionFactor(const std::vector< std::pair< Point, Point > > &bd_side_list, const Point &circle_center, const bool is_closed_loop, const bool move_end_nodes_in_span_direction, Real &c_coeff, Real &end_node_disp) const
Calculates the radius correction factor based on a list of sides on a circular boundary.
CircularBoundaryCorrectionGenerator(const InputParameters &parameters)
std::vector< boundary_id_type > _input_mesh_circular_bids
IDs of the circular boundary of the input mesh.
const std::vector< Real > _custom_circular_tolerance
Customized tolerance used to verify that boundaries are circular.
Real sinePrimeSummation(const std::vector< Real > &th_list, const Real coeff) const
Calculates the first derivative with regards to coeff of the summation of the sine values of a list o...
Real sineSummation(const std::vector< Real > &th_list, const Real coeff) const
Calculates the summation of the sine values of a list of angles.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object.
void addRequiredParam(const std::string &name, const std::string &doc_string)
This method adds a parameter and documentation string to the InputParameters object that will be extr...
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump.
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
MeshGenerators are objects that can modify or add to an existing mesh.
static InputParameters validParams()
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:457
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...
Definition MooseBase.h:271
void isClosedLoop(MeshBase &mesh, Real &max_node_radius, std::vector< dof_id_type > &ordered_node_list, std::vector< std::pair< dof_id_type, dof_id_type > > &node_assm, std::vector< dof_id_type > &midpoint_node_list, const Point origin_pt, const std::string input_type, bool &is_closed_loop, const bool suppress_exception=false)
Decides whether a series of nodes contained in a given mesh forms a closed loop with consecutive node...
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.
bool hasBoundaryNameOrID(const MeshBase &mesh, const BoundaryName &name_or_id)
Whether a particular boundary name or ID exists in the mesh.
std::string stringify(const T &t)
conversion to string
Definition Conversion.h:64
static constexpr Real TOLERANCE
const Real radius