https://mooseframework.inl.gov
Loading...
Searching...
No Matches
FillBetweenPointVectorsTools.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#include "MooseMeshUtils.h"
12#include "MooseMesh.h"
13#include "MeshGenerator.h"
14#include "MooseError.h"
15
16// libMesh includes
17#include "libmesh/int_range.h"
18#include "libmesh/mesh_base.h"
19#include "libmesh/mesh_generation.h"
20#include "libmesh/mesh_serializer.h"
21#include "libmesh/point.h"
22#include "libmesh/elem.h"
23#include "libmesh/node.h"
24#include "libmesh/face_tri3.h"
25#include "libmesh/face_quad4.h"
26
28{
29void
30fillBetweenPointVectorsGenerator(MeshBase & mesh, // an empty mesh is expected
31 const std::vector<Point> & boundary_points_vec_1,
32 const std::vector<Point> & boundary_points_vec_2,
33 const unsigned int num_layers,
34 const subdomain_id_type transition_layer_id,
35 const boundary_id_type input_boundary_1_id,
36 const boundary_id_type input_boundary_2_id,
37 const boundary_id_type begin_side_boundary_id,
38 const boundary_id_type end_side_boundary_id,
39 const std::string type,
40 const std::string name,
41 const bool quad_elem,
42 const Real bias_parameter,
43 const Real sigma)
44{
46 boundary_points_vec_1, Point(0.0, 0.0, 1.0), Point(0.0, 0.0, 0.0)) ||
48 boundary_points_vec_2, Point(0.0, 0.0, 1.0), Point(0.0, 0.0, 0.0)))
49 mooseError("In ",
50 type,
51 " ",
52 name,
53 ", the input vectors of points for "
54 "FillBetweenPointVectorsTools::fillBetweenPointVectorsGenerator "
55 "must be in XY plane.");
56
57 const unsigned int vec_1_node_num = boundary_points_vec_1.size();
58 const unsigned int vec_2_node_num = boundary_points_vec_2.size();
59
60 if (vec_1_node_num < 2 || vec_2_node_num < 2)
61 mooseError("In ",
62 type,
63 " ",
64 name,
65 ", the two input vectors of points for "
66 "FillBetweenPointVectorsTools::fillBetweenPointVectorsGenerator "
67 "must respectively contain at least two elements.");
68
69 if (quad_elem && boundary_points_vec_1.size() != boundary_points_vec_2.size())
70 mooseError("In ",
71 type,
72 " ",
73 name,
74 ", QUAD4 elements option can only be selected when the two input vectors of Points "
75 "have the same length. In the current instance, the first vector has ",
76 boundary_points_vec_1.size(),
77 " points and the second ",
78 boundary_points_vec_2.size(),
79 " points");
80
81 std::vector<Point> possibly_reoriented_boundary_points_vec_2;
82 const std::vector<Point> * oriented_boundary_points_vec_2 = &boundary_points_vec_2;
83
84 if (needFlip(boundary_points_vec_1, boundary_points_vec_2))
85 {
86 possibly_reoriented_boundary_points_vec_2.assign(boundary_points_vec_2.rbegin(),
87 boundary_points_vec_2.rend());
88 oriented_boundary_points_vec_2 = &possibly_reoriented_boundary_points_vec_2;
89
90 // This isn't worth warning about. The way
91 // MooseMeshUtils::makeOrderedNodeList works, we can end up
92 // finding a flip necessary on one element numbering and
93 // unnecessary on another.
94 //
95 // mooseWarning(
96 // "In FillBetweenPointVectorsTools, one of the vector of Points must be flipped to ensure "
97 // "correct transition layer shape.");
98 }
99
100 std::vector<Real> vec_1_index; // Unweighted index
101 std::vector<Real> vec_2_index; // Unweighted index
102
103 std::vector<Real> wt_1;
104 std::vector<Real> index_1; // Weighted index
105 std::vector<Real> wt_2;
106 std::vector<Real> index_2; // Weighted index
107
108 // create interpolations
109 std::unique_ptr<LinearInterpolation> linear_vec_1_x;
110 std::unique_ptr<LinearInterpolation> linear_vec_1_y;
111 std::unique_ptr<SplineInterpolation> spline_vec_1_l;
112 std::unique_ptr<LinearInterpolation> linear_vec_2_x;
113 std::unique_ptr<LinearInterpolation> linear_vec_2_y;
114 std::unique_ptr<SplineInterpolation> spline_vec_2_l;
115
116 weightedInterpolator(vec_1_node_num,
117 boundary_points_vec_1,
118 vec_1_index,
119 wt_1,
120 index_1,
121 sigma,
122 linear_vec_1_x,
123 linear_vec_1_y,
124 spline_vec_1_l);
125 weightedInterpolator(vec_2_node_num,
126 *oriented_boundary_points_vec_2,
127 vec_2_index,
128 wt_2,
129 index_2,
130 sigma,
131 linear_vec_2_x,
132 linear_vec_2_y,
133 spline_vec_2_l);
134
135 // If the two input vectors have different sizes
136 // The node numbers of the intermediate layers change linearly
137 const Real increment = ((Real)vec_2_node_num - (Real)vec_1_node_num) / (Real)(num_layers);
138 // Number of nodes in each sublayer
139 std::vector<unsigned int> node_number_vec;
140 // 2D vector of nodes
141 std::vector<std::vector<Node *>> nodes(num_layers + 1);
142 // Node counter
143 unsigned int node_counter = 0;
144
145 for (const auto i : make_range(num_layers + 1))
146 {
147 // calculate number of nodes in each sublayer
148 node_number_vec.push_back(
149 (unsigned int)(vec_1_node_num + (long)(increment * i + 0.5 - (increment < 0))));
150 // Reserve memory for new nodes
151 nodes[i] = std::vector<Node *>(node_number_vec[i]);
152
153 // Calculate vectors of weighted surrogated index for side #1
154 std::vector<Real> weighted_surrogate_index_1;
155 std::vector<Real> unweighted_surrogate_index_1;
156
157 surrogateGenerator(weighted_surrogate_index_1,
158 unweighted_surrogate_index_1,
159 node_number_vec,
160 wt_1,
161 vec_1_index,
162 vec_1_node_num,
163 i);
164
165 // Calculate vectors of weighted surrogated index for side #2
166 std::vector<Real> weighted_surrogate_index_2;
167 std::vector<Real> unweighted_surrogate_index_2;
168
169 surrogateGenerator(weighted_surrogate_index_2,
170 unweighted_surrogate_index_2,
171 node_number_vec,
172 wt_2,
173 vec_2_index,
174 vec_2_node_num,
175 i);
176
177 for (const auto j : make_range(node_number_vec[i]))
178 {
179 // Create surrogate Points on side #1 for Point #j on the sublayer
180 Point surrogate_pos_1 = Point(linear_vec_1_x->sample(weighted_surrogate_index_1[j]),
181 linear_vec_1_y->sample(weighted_surrogate_index_1[j]),
182 0.0);
183 // Create surrogate Points on side #2 for Point #j on the sublayer
184 Point surrogate_pos_2 = Point(linear_vec_2_x->sample(weighted_surrogate_index_2[j]),
185 linear_vec_2_y->sample(weighted_surrogate_index_2[j]),
186 0.0);
187 const Real l_ratio = bias_parameter <= 0.0
188 ? std::pow(spline_vec_2_l->sample(weighted_surrogate_index_2[j]) /
189 spline_vec_1_l->sample(weighted_surrogate_index_1[j]),
190 1.0 / ((Real)num_layers - 1.0))
191 : bias_parameter;
192 const Real index_factor =
193 MooseUtils::absoluteFuzzyEqual(l_ratio, 1.0)
194 ? (Real)i / (Real)num_layers
195 : (1.0 - std::pow(l_ratio, (Real)i)) / (1.0 - std::pow(l_ratio, (Real)num_layers));
196 Point tmp_point = surrogate_pos_2 * index_factor + surrogate_pos_1 * (1.0 - index_factor);
197 nodes[i][j] = mesh.add_point(tmp_point, j + node_counter);
198 }
199 node_counter += node_number_vec[i];
200 }
201 // Create triangular elements based on the 2D Node vector
202 if (quad_elem)
204 nodes,
205 num_layers,
206 node_number_vec,
207 transition_layer_id,
208 input_boundary_1_id,
209 input_boundary_2_id,
210 begin_side_boundary_id,
211 end_side_boundary_id);
212 else
214 nodes,
215 num_layers,
216 node_number_vec,
217 transition_layer_id,
218 input_boundary_1_id,
219 input_boundary_2_id,
220 begin_side_boundary_id,
221 end_side_boundary_id);
222}
223
224void
226 const std::vector<Point> & boundary_points_vec_1,
227 const std::vector<Point> & boundary_points_vec_2,
228 const unsigned int num_layers,
229 const subdomain_id_type transition_layer_id,
230 const boundary_id_type external_boundary_id,
231 const std::string type,
232 const std::string name,
233 const bool quad_elem)
234{
236 boundary_points_vec_1,
237 boundary_points_vec_2,
238 num_layers,
239 transition_layer_id,
240 external_boundary_id,
241 external_boundary_id,
242 external_boundary_id,
243 external_boundary_id,
244 type,
245 name,
246 quad_elem);
247}
248
249void
251 const std::vector<std::vector<Node *>> & nodes,
252 const unsigned int num_layers,
253 const std::vector<unsigned int> & node_number_vec,
254 const subdomain_id_type transition_layer_id,
255 const boundary_id_type input_boundary_1_id,
256 const boundary_id_type input_boundary_2_id,
257 const boundary_id_type begin_side_boundary_id,
258 const boundary_id_type end_side_boundary_id)
259{
260 const unsigned int node_number = node_number_vec.front();
261 BoundaryInfo & boundary_info = mesh.get_boundary_info();
262
263 for (const auto i : make_range(num_layers))
264 for (unsigned int j = 1; j < node_number; j++)
265 {
266 Elem * elem = mesh.add_elem(new Quad4);
267 bool is_elem_flip = buildQuadElement(elem,
268 nodes[i][j - 1],
269 nodes[i + 1][j - 1],
270 nodes[i + 1][j],
271 nodes[i][j],
272 transition_layer_id);
273 if (i == 0)
274 boundary_info.add_side(elem, is_elem_flip ? 0 : 3, input_boundary_1_id);
275 if (i == num_layers - 1)
276 boundary_info.add_side(elem, is_elem_flip ? 2 : 1, input_boundary_2_id);
277 if (j == 1)
278 boundary_info.add_side(elem, is_elem_flip ? 3 : 0, begin_side_boundary_id);
279 if (j == node_number - 1)
280 boundary_info.add_side(elem, is_elem_flip ? 1 : 2, end_side_boundary_id);
281 }
282}
283
284void
286 const std::vector<std::vector<Node *>> & nodes,
287 const unsigned int num_layers,
288 const std::vector<unsigned int> & node_number_vec,
289 const subdomain_id_type transition_layer_id,
290 const boundary_id_type input_boundary_1_id,
291 const boundary_id_type input_boundary_2_id,
292 const boundary_id_type begin_side_boundary_id,
293 const boundary_id_type end_side_boundary_id)
294{
295 BoundaryInfo & boundary_info = mesh.get_boundary_info();
296
297 for (const auto i : make_range(num_layers))
298 {
299 unsigned int nodes_up_it = 0;
300 unsigned int nodes_down_it = 0;
301 const unsigned int node_number_up = node_number_vec[i + 1];
302 const unsigned int node_number_down = node_number_vec[i];
303
304 while (nodes_up_it < node_number_up - 1 && nodes_down_it < node_number_down - 1 &&
305 nodes_up_it + nodes_down_it < node_number_up + node_number_down - 3)
306 {
307 // Define the two possible options and chose the one with shorter distance
308 Real dis1 = (*nodes[i + 1][nodes_up_it] - *nodes[i][nodes_down_it + 1]).norm();
309 Real dis2 = (*nodes[i + 1][nodes_up_it + 1] - *nodes[i][nodes_down_it]).norm();
310 if (MooseUtils::absoluteFuzzyGreaterThan(dis1, dis2))
311 {
312 Elem * elem = mesh.add_elem(new Tri3);
313 bool is_elem_flip = buildTriElement(elem,
314 nodes[i + 1][nodes_up_it],
315 nodes[i][nodes_down_it],
316 nodes[i + 1][nodes_up_it + 1],
317 transition_layer_id);
318 if (i == num_layers - 1)
319 boundary_info.add_side(elem, is_elem_flip ? 0 : 2, input_boundary_2_id);
320 if (nodes_up_it == 0 && nodes_down_it == 0)
321 boundary_info.add_side(elem, is_elem_flip ? 2 : 0, begin_side_boundary_id);
322 nodes_up_it++;
323 }
324 else
325 {
326 Elem * elem = mesh.add_elem(new Tri3);
327 bool is_elem_flip = buildTriElement(elem,
328 nodes[i + 1][nodes_up_it],
329 nodes[i][nodes_down_it],
330 nodes[i][nodes_down_it + 1],
331 transition_layer_id);
332 if (i == 0)
333 boundary_info.add_side(elem, 1, input_boundary_1_id);
334 if (nodes_up_it == 0 && nodes_down_it == 0)
335 boundary_info.add_side(elem, is_elem_flip ? 2 : 0, begin_side_boundary_id);
336 nodes_down_it++;
337 }
338 }
339 // Handle the end
340 while (nodes_up_it < node_number_up - 1)
341 {
342 Elem * elem = mesh.add_elem(new Tri3);
343 bool is_elem_flip = buildTriElement(elem,
344 nodes[i + 1][nodes_up_it],
345 nodes[i][nodes_down_it],
346 nodes[i + 1][nodes_up_it + 1],
347 transition_layer_id);
348 nodes_up_it++;
349 if (i == num_layers - 1)
350 boundary_info.add_side(elem, is_elem_flip ? 0 : 2, input_boundary_2_id);
351 if (nodes_up_it == node_number_up - 1 && nodes_down_it == node_number_down - 1)
352 boundary_info.add_side(elem, 1, end_side_boundary_id);
353 }
354 while (nodes_down_it < node_number_down - 1)
355 {
356 Elem * elem = mesh.add_elem(new Tri3);
357 bool is_elem_flip = buildTriElement(elem,
358 nodes[i + 1][nodes_up_it],
359 nodes[i][nodes_down_it],
360 nodes[i][nodes_down_it + 1],
361 transition_layer_id);
362 nodes_down_it++;
363 if (i == 0)
364 boundary_info.add_side(elem, 1, input_boundary_1_id);
365 if (nodes_up_it == node_number_up - 1 && nodes_down_it == node_number_down - 1)
366 boundary_info.add_side(elem, is_elem_flip ? 0 : 2, end_side_boundary_id);
367 }
368 }
369}
370
371void
372weightedInterpolator(const unsigned int vec_node_num,
373 const std::vector<Point> & boundary_points_vec,
374 std::vector<Real> & vec_index,
375 std::vector<Real> & wt,
376 std::vector<Real> & index,
377 const Real sigma,
378 std::unique_ptr<LinearInterpolation> & linear_vec_x,
379 std::unique_ptr<LinearInterpolation> & linear_vec_y,
380 std::unique_ptr<SplineInterpolation> & spline_vec_l)
381{
382 std::vector<Real> pos_x;
383 std::vector<Real> pos_y;
384 std::vector<Real> dist_vec;
385 std::vector<Real> pos_l;
386
387 for (const auto i : make_range(vec_node_num))
388 {
389 // Unweighted, the index interval is just uniform
390 // Normalized range 0~1
391 vec_index.push_back((Real)i / ((Real)vec_node_num - 1.0));
392 // X and Y coordinates cooresponding to the index
393 pos_x.push_back(boundary_points_vec[i](0));
394 pos_y.push_back(boundary_points_vec[i](1));
395 // Use Point-to-Point distance as unnormalized weight
396 if (i > 0)
397 {
398 wt.push_back((boundary_points_vec[i] - boundary_points_vec[i - 1]).norm());
399 dist_vec.push_back(wt.back());
400 }
401 // Accumulated unnormalized weights to get unnormalized weighted index
402 index.push_back(std::accumulate(wt.begin(), wt.end(), 0.0));
403 }
404 const Real dist_vec_total = index.back(); // Total accumulated distances
405 const Real wt_norm_factor = dist_vec_total / ((Real)vec_node_num - 1.0); // Normalization factor
406 // Normalization for both weights and weighted indices
407 std::transform(
408 wt.begin(), wt.end(), wt.begin(), [wt_norm_factor](Real & c) { return c / wt_norm_factor; });
409 std::transform(index.begin(),
410 index.end(),
411 index.begin(),
412 [dist_vec_total](Real & c) { return c / dist_vec_total; });
413 // Use Gaussian blurring to smoothen local density
414 for (const auto i : make_range(vec_node_num))
415 {
416 Real gaussian_factor(0.0);
417 Real sum_tmp(0.0);
418 // Use interval as parameter now, consider distance in the future
419 for (const auto j : make_range(vec_node_num - 1))
420 {
421 // dis_vec and index are off by 0.5
422 const Real tmp_factor =
423 exp(-((Real)(i - j) - 0.5) * ((Real)(i - j) - 0.5) / 2.0 / sigma / sigma);
424 gaussian_factor += tmp_factor;
425 sum_tmp += tmp_factor * dist_vec[j];
426 }
427 pos_l.push_back(sum_tmp / gaussian_factor);
428 }
429 // Interpolate positions based on weighted indices
430 linear_vec_x = std::make_unique<LinearInterpolation>(index, pos_x);
431 linear_vec_y = std::make_unique<LinearInterpolation>(index, pos_y);
432 spline_vec_l = std::make_unique<SplineInterpolation>(index, pos_l);
433}
434
435void
436surrogateGenerator(std::vector<Real> & weighted_surrogate_index,
437 std::vector<Real> & unweighted_surrogate_index,
438 const std::vector<unsigned int> & node_number_vec,
439 const std::vector<Real> & wt,
440 const std::vector<Real> & index,
441 const unsigned int boundary_node_num,
442 const unsigned int i)
443{
444 // First element is trivial
445 weighted_surrogate_index.push_back(0.0);
446 unweighted_surrogate_index.push_back(0.0);
447 for (unsigned int j = 1; j < node_number_vec[i]; j++)
448 {
449 // uniform interval for unweighted index
450 unweighted_surrogate_index.push_back((Real)j / ((Real)node_number_vec[i] - 1.0));
451 // >
452 const auto it_0 =
453 std::upper_bound(index.begin(), index.end(), unweighted_surrogate_index[j - 1]);
454 // >=
455 const auto it_1 = std::lower_bound(index.begin(), index.end(), unweighted_surrogate_index[j]);
456 //
457 const auto it_dist = std::distance(it_0, it_1);
458 //
459 const auto it_start = std::distance(index.begin(), it_0);
460
461 if (it_0 == it_1)
462 weighted_surrogate_index.push_back(weighted_surrogate_index[j - 1] +
463 wt[it_start - 1] / ((Real)node_number_vec[i] - 1.0));
464 else
465 {
466 weighted_surrogate_index.push_back(weighted_surrogate_index[j - 1]);
467 weighted_surrogate_index[j] += (*it_0 - unweighted_surrogate_index[j - 1]) * wt[it_start - 1];
468 weighted_surrogate_index[j] +=
469 (unweighted_surrogate_index[j] - *(it_1 - 1)) * wt[it_start + it_dist - 1];
470 for (unsigned int k = 1; k < it_dist; k++)
471 weighted_surrogate_index[j] += wt[it_start + k - 1] / ((Real)boundary_node_num - 1.0);
472 }
473 }
474}
475
476bool
477needFlip(const std::vector<Point> & vec_pts_1, const std::vector<Point> & vec_pts_2)
478{
479 const Real th1 =
480 acos((vec_pts_1.back() - vec_pts_1.front()) * (vec_pts_2.front() - vec_pts_1.front()) /
481 (vec_pts_1.back() - vec_pts_1.front()).norm() /
482 (vec_pts_2.front() - vec_pts_1.front()).norm());
483 const Real th2 = acos(
484 (vec_pts_2.back() - vec_pts_1.back()) * (vec_pts_1.front() - vec_pts_1.back()) /
485 (vec_pts_2.back() - vec_pts_1.back()).norm() / (vec_pts_1.front() - vec_pts_1.back()).norm());
486 const Real th3 = acos(
487 (vec_pts_2.front() - vec_pts_2.back()) * (vec_pts_1.back() - vec_pts_2.back()) /
488 (vec_pts_2.front() - vec_pts_2.back()).norm() / (vec_pts_1.back() - vec_pts_2.back()).norm());
489 const Real th4 =
490 acos((vec_pts_1.front() - vec_pts_2.front()) * (vec_pts_2.back() - vec_pts_2.front()) /
491 (vec_pts_1.front() - vec_pts_2.front()).norm() /
492 (vec_pts_2.back() - vec_pts_2.front()).norm());
493 if (MooseUtils::absoluteFuzzyEqual(th1 + th2 + th3 + th4, 2 * M_PI))
494 return false;
495 return true;
496}
497
498bool
500 Real & max_node_radius,
501 std::vector<dof_id_type> & boundary_ordered_node_list,
502 const Point origin_pt,
503 const boundary_id_type bid)
504{
505 // This has no communication and expects elem_ptr to find any
506 // element, so it only works on serialized meshes
508
509 max_node_radius = 0.0;
510 BoundaryInfo & boundary_info = mesh.get_boundary_info();
511 auto side_list_tmp = boundary_info.build_side_list();
512 std::vector<std::pair<dof_id_type, dof_id_type>> boundary_node_assm;
513 std::vector<dof_id_type> boundary_midpoint_node_list;
514 for (const auto i : index_range(side_list_tmp))
515 {
516 if (std::get<2>(side_list_tmp[i]) == bid)
517 {
518 // store two nodes of each side
519 const auto elem = mesh.elem_ptr(std::get<0>(side_list_tmp[i]));
520 const auto side = elem->side_ptr(std::get<1>(side_list_tmp[i]));
521 boundary_node_assm.push_back(std::make_pair(side->node_id(0), side->node_id(1)));
522 // see if there is a midpoint
523 const auto & side_type = elem->side_type(std::get<1>(side_list_tmp[i]));
524 if (side_type == EDGE3)
525 boundary_midpoint_node_list.push_back(
526 elem->node_id(elem->n_vertices() + std::get<1>(side_list_tmp[i])));
527 else
528 boundary_midpoint_node_list.push_back(DofObject::invalid_id);
529 }
530 }
531 bool is_closed_loop;
533 max_node_radius,
534 boundary_ordered_node_list,
535 boundary_node_assm,
536 boundary_midpoint_node_list,
537 origin_pt,
538 "external boundary",
539 is_closed_loop);
540 return is_closed_loop;
541}
542
543bool
545 Real & max_node_radius,
546 const Point origin_pt,
547 const boundary_id_type bid)
548{
549 std::vector<dof_id_type> dummy_boundary_ordered_node_list;
551 mesh, max_node_radius, dummy_boundary_ordered_node_list, origin_pt, bid);
552}
553
554bool
555isBoundarySimpleClosedLoop(MeshBase & mesh, const Point origin_pt, const boundary_id_type bid)
556{
557 Real dummy_max_node_radius;
558 return isBoundarySimpleClosedLoop(mesh, dummy_max_node_radius, origin_pt, bid);
559}
560
561bool
563 Real & max_node_radius,
564 std::vector<dof_id_type> & boundary_ordered_node_list,
565 const Point origin_pt,
566 const boundary_id_type bid)
567{
568 try
569 {
570 isBoundarySimpleClosedLoop(mesh, max_node_radius, boundary_ordered_node_list, origin_pt, bid);
571 }
572 catch (MooseException & e)
573 {
574 if (((std::string)e.what())
575 .compare("This mesh generator does not work for the provided external boundary as it "
576 "is not a closed loop.") != 0)
577 throw MooseException("The provided boundary is not an open single-segment boundary.");
578 else
579 return true;
580 }
581
582 throw MooseException("The provided boundary is closed loop, which is not supported.");
583}
584
585bool
586isExternalBoundary(MeshBase & mesh, const boundary_id_type bid)
587{
588 // This has no communication and expects elem_ptr to find any
589 // element, so it only works on serialized meshes
591
592 if (!mesh.is_prepared())
593 mesh.find_neighbors();
594 BoundaryInfo & boundary_info = mesh.get_boundary_info();
595 auto side_list = boundary_info.build_side_list();
596 for (const auto i : index_range(side_list))
597 {
598 if (std::get<2>(side_list[i]) == bid)
599 if (mesh.elem_ptr(std::get<0>(side_list[i]))->neighbor_ptr(std::get<1>(side_list[i])) !=
600 nullptr)
601 return false;
602 }
603 return true;
604}
605
606bool
608 Real & max_node_radius,
609 std::vector<dof_id_type> & ordered_node_list,
610 const Point origin_pt)
611{
612 // This has no communication and expects to loop over all elements
613 // on every processor, so it only works on serialized meshes
615
616 max_node_radius = 0.0;
617 std::vector<std::pair<dof_id_type, dof_id_type>> node_assm;
618 for (auto it = mesh.active_elements_begin(); it != mesh.active_elements_end(); it++)
619 node_assm.push_back(std::make_pair((*it)->node_id(0), (*it)->node_id(1)));
620 bool is_closed_loop;
622 mesh, max_node_radius, ordered_node_list, node_assm, origin_pt, "curve", is_closed_loop);
623 return is_closed_loop;
624}
625
626bool
627isCurveSimpleClosedLoop(MeshBase & mesh, Real & max_node_radius, const Point origin_pt)
628{
629 std::vector<dof_id_type> dummy_ordered_node_list;
630 return isCurveSimpleClosedLoop(mesh, max_node_radius, dummy_ordered_node_list, origin_pt);
631}
632
633bool
634isCurveSimpleClosedLoop(MeshBase & mesh, const Point origin_pt)
635{
636 Real dummy_max_node_radius;
637 return isCurveSimpleClosedLoop(mesh, dummy_max_node_radius, origin_pt);
638}
639
640bool
642 Real & max_node_radius,
643 std::vector<dof_id_type> & ordered_node_list,
644 const Point origin_pt)
645{
646 try
647 {
648 isCurveSimpleClosedLoop(mesh, max_node_radius, ordered_node_list, origin_pt);
649 }
650 catch (MooseException & e)
651 {
652 if (((std::string)e.what())
653 .compare("This mesh generator does not work for the provided curve as it is not a "
654 "closed loop.") != 0)
655 throw MooseException("The provided curve is not an open single-segment boundary.");
656 else
657 return true;
658 }
659 throw MooseException("The provided curve is closed loop, which is not supported.");
660 return false;
661}
662
663void
664isClosedLoop(MeshBase & mesh,
665 Real & max_node_radius,
666 std::vector<dof_id_type> & ordered_node_list,
667 std::vector<std::pair<dof_id_type, dof_id_type>> & node_assm,
668 std::vector<dof_id_type> & midpoint_node_list,
669 const Point origin_pt,
670 const std::string input_type,
671 bool & is_closed_loop,
672 const bool suppress_exception)
673{
674 // This has no communication and expects node_ptr to find any
675 // node, so it only works on serialized meshes
677
678 std::vector<dof_id_type> dummy_elem_list = std::vector<dof_id_type>(node_assm.size(), 0);
679 std::vector<dof_id_type> ordered_dummy_elem_list;
680 is_closed_loop = false;
682 node_assm, dummy_elem_list, midpoint_node_list, ordered_node_list, ordered_dummy_elem_list);
683 // If the code ever gets here, node_assm is empty.
684 // If the ordered_node_list front and back are not the same, the boundary is not a loop.
685 // This is not done inside the loop just for some potential applications in the future.
686 if (ordered_node_list.front() != ordered_node_list.back())
687 {
688 // This is invalid type #2
689 if (!suppress_exception)
690 throw MooseException("This mesh generator does not work for the provided ",
691 input_type,
692 " as it is not a closed loop.");
693 }
694 // It the curve is a loop, check if azimuthal angles change monotonically
695 else
696 {
697 // Utilize cross product here.
698 // If azimuthal angles change monotonically,
699 // the z components of the cross products are always negative or positive.
700 std::vector<Real> ordered_node_azi_list;
701 for (const auto i : make_range(ordered_node_list.size() - 1))
702 {
703 ordered_node_azi_list.push_back(
704 (*mesh.node_ptr(ordered_node_list[i]) - origin_pt)
705 .cross(*mesh.node_ptr(ordered_node_list[i + 1]) - origin_pt)(2));
706 // Use this opportunity to calculate maximum radius
707 max_node_radius =
708 std::max((*mesh.node_ptr(ordered_node_list[i]) - origin_pt).norm(), max_node_radius);
709 }
710 std::sort(ordered_node_azi_list.begin(), ordered_node_azi_list.end());
711 if (ordered_node_azi_list.front() * ordered_node_azi_list.back() < 0.0)
712 {
713 // This is invalid type #3
714 if (!suppress_exception)
715 throw MooseException(
716 "This mesh generator does not work for the provided ",
717 input_type,
718 " as azimuthal angles of consecutive nodes do not change monotonically.");
719 }
720 else
721 is_closed_loop = true;
722 }
723}
724
725void
726isClosedLoop(MeshBase & mesh,
727 Real & max_node_radius,
728 std::vector<dof_id_type> & ordered_node_list,
729 std::vector<std::pair<dof_id_type, dof_id_type>> & node_assm,
730 const Point origin_pt,
731 const std::string input_type,
732 bool & is_closed_loop,
733 const bool suppress_exception)
734{
735 std::vector<dof_id_type> dummy_midpoint_node_list(node_assm.size(), DofObject::invalid_id);
737 max_node_radius,
738 ordered_node_list,
739 node_assm,
740 dummy_midpoint_node_list,
741 origin_pt,
742 input_type,
743 is_closed_loop,
744 suppress_exception);
745}
746
747bool
749 Node * nd_0,
750 Node * nd_1,
751 Node * nd_2,
752 Node * nd_3,
753 const subdomain_id_type transition_layer_id)
754{
755 // Adjust the order of nodes in an element so that the mesh can be extruded in (0 0 1)
756 // direction.
757 elem->subdomain_id() = transition_layer_id;
758 if (((*nd_1 - *nd_0).cross((*nd_2 - *nd_0)).unit())(2) > 0)
759 {
760 elem->set_node(0, nd_0);
761 elem->set_node(1, nd_1);
762 elem->set_node(2, nd_2);
763 elem->set_node(3, nd_3);
764 return false;
765 }
766 else
767 {
768 elem->set_node(0, nd_0);
769 elem->set_node(3, nd_1);
770 elem->set_node(2, nd_2);
771 elem->set_node(1, nd_3);
772 return true;
773 }
774}
775
776bool
778 Elem * elem, Node * nd_0, Node * nd_1, Node * nd_2, const subdomain_id_type transition_layer_id)
779{
780 // Adjust the order of nodes in an element so that the mesh can be extruded in (0 0 1)
781 // direction.
782 elem->subdomain_id() = transition_layer_id;
783 if (((*nd_1 - *nd_0).cross((*nd_2 - *nd_0)).unit())(2) > 0)
784 {
785 elem->set_node(0, nd_0);
786 elem->set_node(1, nd_1);
787 elem->set_node(2, nd_2);
788 return false;
789 }
790 else
791 {
792 elem->set_node(0, nd_0);
793 elem->set_node(2, nd_1);
794 elem->set_node(1, nd_2);
795 return true;
796 }
797}
798}
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
Provides a way for users to bail out of the current solve.
virtual const char * what() const
Get out the error message.
MeshBase & mesh
bool isCurveSimpleClosedLoop(MeshBase &mesh, Real &max_node_radius, std::vector< dof_id_type > &ordered_node_list, const Point origin_pt)
Decides whether a curve contained in a given mesh is a closed loop with consecutive nodes's azimuthal...
void elementsCreationFromNodesVectorsQuad(MeshBase &mesh, const std::vector< std::vector< Node * > > &nodes, const unsigned int num_layers, const std::vector< unsigned int > &node_number_vec, const subdomain_id_type transition_layer_id, const boundary_id_type input_boundary_1_id, const boundary_id_type input_boundary_2_id, const boundary_id_type begin_side_boundary_id, const boundary_id_type end_side_boundary_id)
Generates a 2D mesh based on a 2D vector of Nodes using QUAD4 elements in the xy-plane.
bool isBoundarySimpleClosedLoop(MeshBase &mesh, Real &max_node_radius, std::vector< dof_id_type > &boundary_ordered_node_list, const Point origin_pt, const boundary_id_type bid)
Decides whether a boundary of a given mesh is a closed loop with consecutive nodes's azimuthal angles...
void weightedInterpolator(const unsigned int vec_node_num, const std::vector< Point > &boundary_points_vec, std::vector< Real > &vec_index, std::vector< Real > &wt, std::vector< Real > &index, const Real sigma, std::unique_ptr< LinearInterpolation > &linear_vec_x, std::unique_ptr< LinearInterpolation > &linear_vec_y, std::unique_ptr< SplineInterpolation > &spline_vec_l)
Generates weights, weighted indices and corresponding interpolation.
void elementsCreationFromNodesVectors(MeshBase &mesh, const std::vector< std::vector< Node * > > &nodes, const unsigned int num_layers, const std::vector< unsigned int > &node_number_vec, const subdomain_id_type transition_layer_id, const boundary_id_type input_boundary_1_id, const boundary_id_type input_boundary_2_id, const boundary_id_type begin_side_boundary_id, const boundary_id_type end_side_boundary_id)
Generates a 2D mesh based on a 2D vector of Nodes using TRI3 elements in the xy-plane.
bool isBoundaryOpenSingleSegment(MeshBase &mesh, Real &max_node_radius, std::vector< dof_id_type > &boundary_ordered_node_list, const Point origin_pt, const boundary_id_type bid)
Decides whether a boundary of a given mesh is an open single-segment boundary.
bool isCurveOpenSingleSegment(MeshBase &mesh, Real &max_node_radius, std::vector< dof_id_type > &ordered_node_list, const Point origin_pt)
Decides whether a curve contained in a given mesh is an open single-segment curve.
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...
void surrogateGenerator(std::vector< Real > &weighted_surrogate_index, std::vector< Real > &unweighted_surrogate_index, const std::vector< unsigned int > &node_number_vec, const std::vector< Real > &index, const std::vector< Real > &wt, const unsigned int boundary_node_num, const unsigned int i)
Generates weighted surrogate index vectors on one side for points of a sublayer curve.
bool buildTriElement(Elem *elem, Node *nd_0, Node *nd_1, Node *nd_2, const subdomain_id_type transition_layer_id)
Creates an TRI3 element that can be extruded in (0 0 1) direction.
bool isExternalBoundary(MeshBase &mesh, const boundary_id_type bid)
Decides whether a boundary of a given mesh is an external boundary.
bool needFlip(const std::vector< Point > &vec_pts_1, const std::vector< Point > &vec_pts_2)
Decide whether one of the input vector of Points needs to be flipped to ensure correct transition lay...
void fillBetweenPointVectorsGenerator(MeshBase &mesh, const std::vector< Point > &boundary_points_vec_1, const std::vector< Point > &boundary_points_vec_2, const unsigned int num_layers, const subdomain_id_type transition_layer_id, const boundary_id_type input_boundary_1_id, const boundary_id_type input_boundary_2_id, const boundary_id_type begin_side_boundary_id, const boundary_id_type end_side_boundary_id, const std::string type, const std::string name, const bool quad_elem=false, const Real bias_parameter=1.0, const Real sigma=3.0)
Generates a 2D mesh with triangular elements for a region defined by two curves (sets of Points)
bool buildQuadElement(Elem *elem, Node *nd_0, Node *nd_1, Node *nd_2, Node *nd_3, const subdomain_id_type transition_layer_id)
Creates an QUAD4 element that can be extruded in (0 0 1) direction.
bool isCoPlanar(const std::vector< Point > &vec_pts, const Point plane_nvec, const Point fixed_pt)
Decides whether all the Points of a vector of Points are in a plane that is defined by a normal vecto...
void makeOrderedNodeList(std::vector< std::pair< dof_id_type, dof_id_type > > &node_assm, std::vector< dof_id_type > &elem_id_list, std::vector< dof_id_type > &midpoint_node_list, std::vector< dof_id_type > &ordered_node_list, std::vector< dof_id_type > &ordered_elem_id_list)
Convert a list of sides in the form of a vector of pairs of node ids into a list of ordered nodes bas...
MooseUnits pow(const MooseUnits &, int)
Definition Units.C:537