https://mooseframework.inl.gov
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 
27 using namespace libMesh;
28 
30 {
31 void
32 fillBetweenPointVectorsGenerator(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 
226 void
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 
251 void
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 
286 void
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 
373 void
374 weightedInterpolator(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 
437 void
438 surrogateGenerator(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 
478 bool
479 needFlip(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 
500 bool
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 
545 bool
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 
556 bool
557 isBoundarySimpleClosedLoop(MeshBase & mesh, const Point origin_pt, const boundary_id_type bid)
558 {
559  Real dummy_max_node_radius;
560  return isBoundarySimpleClosedLoop(mesh, dummy_max_node_radius, origin_pt, bid);
561 }
562 
563 bool
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 
587 bool
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 
608 bool
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;
623  isClosedLoop(
624  mesh, max_node_radius, ordered_node_list, node_assm, origin_pt, "curve", is_closed_loop);
625  return is_closed_loop;
626 }
627 
628 bool
629 isCurveSimpleClosedLoop(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 
635 bool
636 isCurveSimpleClosedLoop(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 
642 bool
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 
665 void
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 
727 void
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 
749 bool
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 
778 bool
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 }
std::string name(const ElemQuality q)
bool is_prepared() const
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...
virtual Node *& set_node(const unsigned int i)
constexpr auto increment(std::index_sequence< first, tail... >)
Increment the first number in an index sequence, but roll over into the next number if it reaches Nma...
virtual const char * what() const
Get out the error message.
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether two variables are equal within an absolute tolerance.
Definition: MooseUtils.h:380
auto exp(const T &)
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:302
MeshBase & mesh
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 external_boundary_id, const std::string type, const std::string name, const bool quad_elem)
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, const Point origin_pt, const std::string input_type, bool &is_closed_loop, const bool suppress_exception)
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
const BoundaryInfo & get_boundary_info() const
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
bool needFlip(const std::vector< Point > &vec_pts_1, const std::vector< Point > &vec_pts_2)
auto max(const L &left, const R &right)
void build_side_list(std::vector< dof_id_type > &element_id_list, std::vector< unsigned short int > &side_list, std::vector< boundary_id_type > &bc_id_list) const
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)
virtual void find_neighbors(const bool reset_remote_elements=false, const bool reset_current_list=true)=0
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)
int8_t boundary_id_type
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.
virtual Elem * add_elem(Elem *e)=0
static const dof_id_type invalid_id
bool buildQuadElement(Elem *elem, Node *nd_0, Node *nd_1, Node *nd_2, Node *nd_3, const subdomain_id_type transition_layer_id)
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...
auto norm(const T &a) -> decltype(std::abs(a))
bool isCurveOpenSingleSegment(MeshBase &mesh, Real &max_node_radius, std::vector< dof_id_type > &ordered_node_list, const Point origin_pt)
virtual const Elem * elem_ptr(const dof_id_type i) const=0
const Elem * neighbor_ptr(unsigned int i) const
Provides a way for users to bail out of the current solve.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
subdomain_id_type subdomain_id() const
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)
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
IntRange< T > make_range(T beg, T end)
bool isCurveSimpleClosedLoop(MeshBase &mesh, const Point origin_pt)
bool buildTriElement(Elem *elem, Node *nd_0, Node *nd_1, Node *nd_2, const subdomain_id_type transition_layer_id)
virtual const Node * node_ptr(const dof_id_type i) const=0
bool isExternalBoundary(MeshBase &mesh, const boundary_id_type bid)
MooseUnits pow(const MooseUnits &, int)
Definition: Units.C:537
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)
bool isBoundarySimpleClosedLoop(MeshBase &mesh, const Point origin_pt, const boundary_id_type bid)
auto index_range(const T &sizable)
bool absoluteFuzzyGreaterThan(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Function to check whether a variable is greater than another variable within an absolute tolerance...
Definition: MooseUtils.h:428