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