https://mooseframework.inl.gov
PeripheralRingMeshGenerator.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
11 
12 #include "MooseMeshUtils.h"
13 #include "MooseUtils.h"
14 #include "LinearInterpolation.h"
17 #include "libmesh/int_range.h"
18 
19 // C++ includes
20 #include <cmath> // for atan2
21 
23 
26 {
28  params.addRequiredParam<MeshGeneratorName>("input", "The input mesh to be modified.");
29  params.addParam<bool>(
30  "force_input_centroid_as_center",
31  false,
32  "Whether to enforce use of the centroid position of the input mesh as the "
33  "center of the peripheral ring by translating the input mesh to the origin.");
34 
35  params.addRangeCheckedParam<unsigned int>(
36  "peripheral_layer_num",
37  3,
38  "peripheral_layer_num>0",
39  "The radial layers of the peripheral ring to be added.");
40  params.addRangeCheckedParam<Real>(
41  "peripheral_radial_bias",
42  1.0,
43  "peripheral_radial_bias>0",
44  "Value used to create biasing in radial meshing for peripheral ring region.");
45  params.addRangeCheckedParam<Real>(
46  "peripheral_inner_boundary_layer_width",
47  0.0,
48  "peripheral_inner_boundary_layer_width>=0",
49  "Width of peripheral ring region that is assigned to be the inner boundary layer.");
50  params.addRangeCheckedParam<unsigned int>(
51  "peripheral_inner_boundary_layer_intervals",
52  1,
53  "peripheral_inner_boundary_layer_intervals>0",
54  "Number of radial intervals of the peripheral ring inner boundary layer");
55  params.addRangeCheckedParam<Real>(
56  "peripheral_inner_boundary_layer_bias",
57  1.0,
58  "peripheral_inner_boundary_layer_bias>0",
59  "Growth factor used for mesh biasing of the peripheral ring inner boundary layer.");
60  params.addRangeCheckedParam<Real>(
61  "peripheral_outer_boundary_layer_width",
62  0.0,
63  "peripheral_outer_boundary_layer_width>=0",
64  "Width of peripheral ring region that is assigned to be the outer boundary layer.");
65  params.addRangeCheckedParam<unsigned int>(
66  "peripheral_outer_boundary_layer_intervals",
67  1,
68  "peripheral_outer_boundary_layer_intervals>0",
69  "Number of radial intervals of the peripheral ring outer boundary layer");
70  params.addRangeCheckedParam<Real>(
71  "peripheral_outer_boundary_layer_bias",
72  1.0,
73  "peripheral_outer_boundary_layer_bias>0",
74  "Growth factor used for mesh biasing of the peripheral ring outer boundary layer.");
75  params.addRequiredRangeCheckedParam<Real>("peripheral_ring_radius",
76  "peripheral_ring_radius>0",
77  "Radius of the peripheral ring to be added.");
78  params.addParam<bool>(
79  "preserve_volumes",
80  true,
81  "Whether the volume of the peripheral region is preserved by fixing the radius.");
82  params.addRequiredParam<BoundaryName>("input_mesh_external_boundary",
83  "The external boundary of the input mesh.");
85  "peripheral_ring_block_id", "The block id assigned to the created peripheral layer.");
86  params.addParam<SubdomainName>("peripheral_ring_block_name",
87  "The block name assigned to the created peripheral layer.");
88  params.addRangeCheckedParam<boundary_id_type>("external_boundary_id",
89  "external_boundary_id>0",
90  "Optional customized external boundary id.");
91  params.addParam<BoundaryName>(
92  "external_boundary_name", "", "Optional customized external boundary name.");
93  params.addParamNamesToGroup(
94  "peripheral_radial_bias peripheral_inner_boundary_layer_width "
95  "peripheral_inner_boundary_layer_intervals peripheral_inner_boundary_layer_bias "
96  "peripheral_outer_boundary_layer_width peripheral_outer_boundary_layer_intervals "
97  "peripheral_outer_boundary_layer_bias",
98  "Mesh Boundary Layer and Biasing Options");
99  params.addClassDescription("This PeripheralRingMeshGenerator object adds a circular peripheral "
100  "region to the input mesh.");
101 
102  return params;
103 }
104 
106  : PolygonMeshGeneratorBase(parameters),
107  _input_name(getParam<MeshGeneratorName>("input")),
108  _force_input_centroid_as_center(getParam<bool>("force_input_centroid_as_center")),
109  _peripheral_layer_num(getParam<unsigned int>("peripheral_layer_num")),
110  _peripheral_radial_bias(getParam<Real>("peripheral_radial_bias")),
111  _peripheral_inner_boundary_layer_params(
112  {getParam<Real>("peripheral_inner_boundary_layer_width"),
113  0.0,
114  getParam<Real>("peripheral_inner_boundary_layer_width") > 0.0
115  ? getParam<unsigned int>("peripheral_inner_boundary_layer_intervals")
116  : 0,
117  getParam<Real>("peripheral_inner_boundary_layer_bias")}),
118  _peripheral_outer_boundary_layer_params(
119  {getParam<Real>("peripheral_outer_boundary_layer_width"),
120  0.0,
121  getParam<Real>("peripheral_outer_boundary_layer_width") > 0.0
122  ? getParam<unsigned int>("peripheral_outer_boundary_layer_intervals")
123  : 0,
124  getParam<Real>("peripheral_outer_boundary_layer_bias")}),
125  _peripheral_ring_radius(getParam<Real>("peripheral_ring_radius")),
126  _preserve_volumes(getParam<bool>("preserve_volumes")),
127  _input_mesh_external_boundary(getParam<BoundaryName>("input_mesh_external_boundary")),
128  _peripheral_ring_block_id(getParam<subdomain_id_type>("peripheral_ring_block_id")),
129  _peripheral_ring_block_name(isParamValid("peripheral_ring_block_name")
130  ? getParam<SubdomainName>("peripheral_ring_block_name")
131  : (SubdomainName) ""),
132  _external_boundary_id(isParamValid("external_boundary_id")
133  ? getParam<boundary_id_type>("external_boundary_id")
134  : 0),
135  _external_boundary_name(getParam<BoundaryName>("external_boundary_name")),
136  _input(getMeshByName(_input_name))
137 {
138  declareMeshProperty<bool>("hexagon_peripheral_trimmability", false);
139  declareMeshProperty<bool>("hexagon_center_trimmability", false);
140  declareMeshProperty<bool>("square_peripheral_trimmability", false);
141  declareMeshProperty<bool>("square_center_trimmability", false);
142 }
143 
144 std::unique_ptr<MeshBase>
146 {
147  if (hasMeshProperty<bool>("hexagon_center_trimmability", _input_name))
148  setMeshProperty("hexagon_center_trimmability",
149  getMeshProperty<bool>("hexagon_center_trimmability", _input_name));
150  if (hasMeshProperty<bool>("square_center_trimmability", _input_name))
151  setMeshProperty("square_center_trimmability",
152  getMeshProperty<bool>("square_center_trimmability", _input_name));
153 
154  // Need ReplicatedMesh for stitching
155  auto input_mesh = dynamic_cast<ReplicatedMesh *>(_input.get());
156  if (!input_mesh)
157  paramError("input", "Input is not a replicated mesh, which is required.");
158 
159  if (!input_mesh->preparation().has_cached_elem_data)
160  input_mesh->cache_elem_data();
161  if (*(input_mesh->elem_dimensions().begin()) != 2 ||
162  *(input_mesh->elem_dimensions().rbegin()) != 2)
163  paramError("input", "Only 2D meshes are supported.");
164 
168  paramError("input_mesh_external_boundary",
169  "External boundary does not exist in the input mesh");
170  // We check the element types of input mesh's external boundary here.
171  // We support both linear and quadratic sides (i.e., EDGE2 and EDGE3), but we cannot support mixed
172  // sides
173  auto side_list_tmp = input_mesh->get_boundary_info().build_side_list();
174  bool linear_side = false;
175  bool quadratic_side = false;
176  for (const auto & side : side_list_tmp)
177  {
178  if (std::get<2>(side) == _input_mesh_external_bid)
179  {
180  const auto etype = input_mesh->elem_ptr(std::get<0>(side))->side_type(std::get<1>(side));
181  if (etype == EDGE2)
182  linear_side = true;
183  else if (etype == EDGE3)
184  quadratic_side = true;
185  else
186  paramError("input", "Input contains elements that are not supported by this generator.");
187  }
188  }
189  if (linear_side && quadratic_side)
190  paramError("input", "Input contains mixed element types on the external boundary.");
191  const unsigned int order = linear_side ? 1 : 2;
192  if (order == 2)
193  {
201  }
202 
203  // Move the centroid of the mesh to (0, 0, 0) if input centroid is enforced to be the ring center.
204  const Point input_mesh_centroid = MooseMeshUtils::meshCentroidCalculator(*input_mesh);
206  MeshTools::Modification::translate(
207  *input_mesh, -input_mesh_centroid(0), -input_mesh_centroid(1), -input_mesh_centroid(2));
208 
209  // Use CoM of the input mesh as its origin for azimuthal calculation
210  const Point origin_pt =
211  _force_input_centroid_as_center ? Point(0.0, 0.0, 0.0) : input_mesh_centroid;
212  // Vessel for containing maximum radius of the boundary nodes
213  Real max_input_mesh_node_radius;
214 
215  // Calculate biasing terms
216  // For 2nd order mesh, as we need the mid points, the bias factor is square rooted.
217  const auto main_peripheral_bias_terms =
219  const auto inner_peripheral_bias_terms =
222  // It is easier to create outer boundary layer inversely (inwards). Thus, 1.0 / bias is used here.
223  // However, the input parameter definition is not affected.
224  const auto outer_peripheral_bias_terms =
227 
228  const unsigned int total_peripheral_layer_num =
231 
232  // Collect the external boundary nodes of the input mesh using the utility function
233  std::vector<dof_id_type> boundary_ordered_node_list;
234  try
235  {
237  max_input_mesh_node_radius,
238  boundary_ordered_node_list,
239  origin_pt,
241  }
242  catch (MooseException & e)
243  {
244  if (((std::string)e.what()).compare("The node list provided has more than one segments.") == 0)
245  paramError("input_mesh_external_boundary",
246  "This mesh generator does not work for the provided external boundary as it has "
247  "more than one segments.");
248  else
249  paramError("input_mesh_external_boundary", e.what());
250  }
251 
252  if (max_input_mesh_node_radius >= _peripheral_ring_radius)
253  paramError(
254  "peripheral_ring_radius",
255  "The peripheral ring to be generated must be large enough to cover the entire input mesh.");
257  paramError("input_mesh_external_boundary",
258  "The boundary provided is not an external boundary.");
259 
260  std::vector<Point> input_ext_bd_pts;
261  std::vector<Point> output_ext_bd_pts;
262  std::vector<std::tuple<Real, Point, Point>> azi_points;
263  std::vector<Real> azi_array;
264  Real tmp_azi(0.0);
265  auto mesh = buildReplicatedMesh(2);
266 
267  // Node counter for the external boundary
268  unsigned int input_ext_node_num = 0;
269 
270  // As the node list collected before is a simple closed loop, the last node is the same as the
271  // first node. We remove it here.
272  boundary_ordered_node_list.pop_back();
273  // Loop over all the boundary nodes
274  // For quadratic sides, the middle points are included automatically
275  for (const auto i : index_range(boundary_ordered_node_list))
276  {
277  input_ext_node_num++;
278  // Define nodes on the inner and outer boundaries of the peripheral region.
279  input_ext_bd_pts.push_back(input_mesh->point(boundary_ordered_node_list[i]));
280  tmp_azi =
281  atan2(input_ext_bd_pts.back()(1) - origin_pt(1), input_ext_bd_pts.back()(0) - origin_pt(0));
282  output_ext_bd_pts.push_back(Point(_peripheral_ring_radius * std::cos(tmp_azi),
283  _peripheral_ring_radius * std::sin(tmp_azi),
284  origin_pt(2)));
285  // a vector of tuples using azimuthal angle as the key to facilitate sorting
286  azi_points.push_back(
287  std::make_tuple(tmp_azi, input_ext_bd_pts.back(), output_ext_bd_pts.back()));
288  // a simple vector of azimuthal angles for radius correction purposes (in degree)
289  azi_array.push_back(tmp_azi / M_PI * 180.0);
290  }
291  // Only for quadratic sides, if the index of the first node after sorting is even, it is a vertex
292  // node; otherwise, it is a midpoint node.
293  const bool is_first_node_vertex =
294  order == 1 ||
295  !(std::distance(azi_array.begin(), std::min_element(azi_array.begin(), azi_array.end())) % 2);
296  std::sort(azi_points.begin(), azi_points.end());
297  std::sort(azi_array.begin(), azi_array.end());
298 
299  // Angles defined by three neighboring nodes on input mesh's external boundary
300  std::vector<Real> input_bdry_angles;
301  // Normal directions of input boundary nodes
302  std::vector<Point> input_bdry_nd;
303  for (const auto i : index_range(azi_points))
304  {
305  Point p1, p2;
306  const Point pn = Point(0.0, 0.0, 1.0);
307  if (i == 0)
308  {
309  p1 = std::get<1>(azi_points[i + 1]) - std::get<1>(azi_points[i]);
310  p2 = std::get<1>(azi_points.back()) - std::get<1>(azi_points[i]);
311  }
312  else if (i == azi_points.size() - 1)
313  {
314  p1 = std::get<1>(azi_points.front()) - std::get<1>(azi_points.back());
315  p2 = std::get<1>(azi_points[i - 1]) - std::get<1>(azi_points.back());
316  }
317  else
318  {
319  p1 = std::get<1>(azi_points[i + 1]) - std::get<1>(azi_points[i]);
320  p2 = std::get<1>(azi_points[i - 1]) - std::get<1>(azi_points[i]);
321  }
322  // Use cross point to get perpendicular direction
323  const Point p1n = (p1.cross(pn)).unit();
324  const Point p2n = -(p2.cross(pn)).unit();
325  Real tmp = p1 * p2 / p1.norm() / p2.norm();
326  // Make sure acos() gets valid input
327  tmp = tmp > 1.0 ? 1.0 : (tmp < -1.0 ? -1.0 : tmp);
328  input_bdry_angles.push_back(acos(tmp) / 2.0);
329  input_bdry_nd.push_back((p1n + p2n).unit());
330  }
331 
332  // 2D vector containing all the node positions of the peripheral region
333  std::vector<std::vector<Point>> points_array(total_peripheral_layer_num + 1,
334  std::vector<Point>(input_ext_node_num));
335  // 2D vector containing all the node ids of the peripheral region
336  std::vector<std::vector<dof_id_type>> node_id_array(total_peripheral_layer_num + 1,
337  std::vector<dof_id_type>(input_ext_node_num));
338  // Reference outmost layer of inner boundary layer
339  std::vector<Point> ref_inner_bdry_surf;
340  // First loop
341  for (const auto i : make_range(input_ext_node_num))
342  {
343  // Inner boundary nodes of the peripheral region
344  points_array[0][i] = std::get<1>(azi_points[i]);
345  // Define outer layer of inner boundary layer
347  {
348  // Outside point of the inner boundary layer
349  const Point ref_inner_boundary_shift =
350  (_peripheral_inner_boundary_layer_params.width / sin(input_bdry_angles[i])) *
351  input_bdry_nd[i];
352  ref_inner_bdry_surf.push_back(points_array[0][i] + ref_inner_boundary_shift);
353  }
354  }
355 
357  innerBdryLayerNodesDefiner(input_ext_node_num,
358  input_bdry_angles,
359  ref_inner_bdry_surf,
360  inner_peripheral_bias_terms,
361  azi_array,
362  origin_pt,
363  points_array);
364  const Real correction_factor = _preserve_volumes
366  azi_array, true, order, is_first_node_vertex)
367  : 1.0;
368  // Loop to handle outer boundary layer and main region
369  for (const auto i : make_range(input_ext_node_num))
370  {
371  // Outer boundary nodes of the peripheral region
372  points_array[total_peripheral_layer_num][i] = std::get<2>(azi_points[i]) * correction_factor;
373  // Outer boundary layer points
375  {
376  // Inner point of the outer boundary layer
377  const Point outer_boundary_shift =
378  -Point(std::cos(std::get<0>(azi_points[i])), std::sin(std::get<0>(azi_points[i])), 0.0) *
380  for (const auto j :
382  points_array[total_peripheral_layer_num - j][i] =
383  points_array[total_peripheral_layer_num][i] +
384  outer_boundary_shift * outer_peripheral_bias_terms[j - 1];
385  }
386  // Use interpolation to get main region
387  if (MooseUtils::absoluteFuzzyGreaterEqual(
388  (points_array[_peripheral_inner_boundary_layer_params.intervals][i] - origin_pt).norm(),
390  _peripheral_layer_num * order][i] -
391  origin_pt)
392  .norm()))
393  {
394  paramError("peripheral_inner_boundary_layer_width",
395  "The summation of peripheral_inner_boundary_layer_width and "
396  "peripheral_outer_boundary_layer_width must be smaller than the thickness of "
397  "peripheral ring region.");
398  }
399 
400  for (const auto j : make_range(uint(1), _peripheral_layer_num * order))
403  (1.0 - main_peripheral_bias_terms[j - 1]) +
405  _peripheral_layer_num * order][i] *
406  main_peripheral_bias_terms[j - 1];
407  }
408  unsigned int num_total_nodes = (total_peripheral_layer_num + 1) * input_ext_node_num;
409  std::vector<Node *> nodes(num_total_nodes); // reserve nodes pointers
410  dof_id_type node_id = 0;
411 
412  // Add nodes to the new mesh
413  for (const auto i : make_range(total_peripheral_layer_num + 1))
414  for (const auto j : make_range(input_ext_node_num))
415  {
416  nodes[node_id] = mesh->add_point(points_array[i][j], node_id);
417  node_id_array[i][j] = node_id;
418  node_id++;
419  }
420  // Add elements to the new mesh
421  BoundaryInfo & boundary_info = mesh->get_boundary_info();
422  const unsigned int index_shift = is_first_node_vertex ? 0 : 1;
423  for (const auto i : make_range(total_peripheral_layer_num / order))
424  for (const auto j : make_range(input_ext_node_num / order))
425  {
426  std::unique_ptr<Elem> new_elem;
427  new_elem = std::make_unique<Quad4>();
428  if (order == 2)
429  {
430  new_elem = std::make_unique<Quad9>();
431  new_elem->set_node(4, nodes[node_id_array[i * order + 1][j * order + index_shift]]);
432  new_elem->set_node(5,
433  nodes[node_id_array[(i + 1) * order][(j * order + 1 + index_shift) %
434  input_ext_node_num]]);
435  new_elem->set_node(6,
436  nodes[node_id_array[i * order + 1][((j + 1) * order + index_shift) %
437  input_ext_node_num]]);
438  new_elem->set_node(
439  7, nodes[node_id_array[i * order][(j * order + 1 + index_shift) % input_ext_node_num]]);
440  new_elem->set_node(8,
441  nodes[node_id_array[i * order + 1][(j * order + 1 + index_shift) %
442  input_ext_node_num]]);
443  }
444  new_elem->set_node(0, nodes[node_id_array[i * order][j * order + index_shift]]);
445  new_elem->set_node(1, nodes[node_id_array[(i + 1) * order][j * order + index_shift]]);
446  new_elem->set_node(2,
447  nodes[node_id_array[(i + 1) * order][((j + 1) * order + index_shift) %
448  input_ext_node_num]]);
449  new_elem->set_node(
450  3, nodes[node_id_array[i * order][((j + 1) * order + index_shift) % input_ext_node_num]]);
451  new_elem->subdomain_id() = _peripheral_ring_block_id;
452 
453  Elem * added_elem = mesh->add_elem(std::move(new_elem));
454 
455  if (i == 0)
456  boundary_info.add_side(added_elem, 3, OUTER_SIDESET_ID_ALT);
457  if (i == total_peripheral_layer_num / order - 1)
458  boundary_info.add_side(added_elem, 1, OUTER_SIDESET_ID);
459  }
460 
461  // This would make sure that the boundary OUTER_SIDESET_ID is deleted after stitching.
464  mesh->prepare_for_use();
465  // Use input_mesh here to retain the subdomain name map
466  input_mesh->stitch_meshes(*mesh, OUTER_SIDESET_ID, OUTER_SIDESET_ID_ALT, TOLERANCE, true, false);
467 
468  // Assign subdomain name to the new block if applicable
469  if (isParamValid("peripheral_ring_block_name"))
470  input_mesh->subdomain_name(_peripheral_ring_block_id) = _peripheral_ring_block_name;
471  // Assign customized external boundary id
472  if (_external_boundary_id > 0)
474  // Assign customized external boundary name
475  if (!_external_boundary_name.empty())
476  {
477  input_mesh->get_boundary_info().sideset_name(
480  input_mesh->get_boundary_info().nodeset_name(
483  }
484 
485  _input->unset_is_prepared();
486  return dynamic_pointer_cast<MeshBase>(_input);
487 }
488 
489 void
491  const unsigned int input_ext_node_num,
492  const std::vector<Real> input_bdry_angles,
493  const std::vector<Point> ref_inner_bdry_surf,
494  const std::vector<Real> inner_peripheral_bias_terms,
495  const std::vector<Real> azi_array,
496  const Point origin_pt,
497  std::vector<std::vector<Point>> & points_array) const
498 {
499  // Check if any azimuthal angles are messed after inner boundary layer addition
500  std::vector<bool> delete_mark(input_ext_node_num, false);
501  std::vector<Real> interp_azi_data, interp_x_data, interp_y_data;
502  std::unique_ptr<LinearInterpolation> linterp_x, linterp_y;
503  // Mark the to-be-deleted elements
504  for (const auto i : make_range(input_ext_node_num))
505  {
506  // For a zig-zag external boundary, when we add a conformal layer onto it by translating the
507  // nodes in the surface normal direction, it is possible that the azimuthal order is flipped.
508  // As shown below, o's are the original boundary nodes, and *'s are the nodes after
509  // translation by the boundary layer thickness.
510  // * *
511  // | | | /
512  // o o-------/--*
513  // | | / |
514  // | outside --> | / |
515  // | | / |
516  // o--------o-- o-------o--
517  // To mitigate this flipping issue, we check the node flipping here using the cross product of
518  // neighboring node-to-origin vectors. Flipped nodes are marked and excluded during the
519  // follow-up interpolation.
520  if (!MooseUtils::absoluteFuzzyEqual(input_bdry_angles[i], M_PI / 2.0, 1.0e-4))
521  {
522  unsigned int index_shift = 1;
523  bool minus_shift_flag = true;
524  bool plus_shift_flag = true;
525  // Need to check both directions for multiple shifts
526  // Half of the external boundary nodes are used as an upper limit to avert infinite loops
527  while (index_shift < input_ext_node_num / 2 && (minus_shift_flag || plus_shift_flag))
528  {
529  if (((ref_inner_bdry_surf[MathUtils::euclideanMod(((int)i - (int)index_shift),
530  (int)input_ext_node_num)] -
531  origin_pt)
532  .cross(ref_inner_bdry_surf[i] - origin_pt))(2) <= 0.0 &&
533  minus_shift_flag)
534  delete_mark[MathUtils::euclideanMod(((int)i - (int)index_shift),
535  (int)input_ext_node_num)] = true;
536  else
537  minus_shift_flag = false;
538  if (((ref_inner_bdry_surf[(i + index_shift) % input_ext_node_num] - origin_pt)
539  .cross(ref_inner_bdry_surf[i] - origin_pt))(2) >= 0.0 &&
540  plus_shift_flag)
541  delete_mark[(i + index_shift) % input_ext_node_num] = true;
542  else
543  plus_shift_flag = false;
544  index_shift++;
545  }
546  }
547  }
548  // Create vectors for interpolation
549  // Due to the flip issue, linear interpolation is used here to mark the location of the boundary
550  // layer's outer boundary.
551  for (const auto i : make_range(input_ext_node_num))
552  {
553  if (!delete_mark[i])
554  {
555  interp_azi_data.push_back(atan2(ref_inner_bdry_surf[i](1) - origin_pt(1),
556  ref_inner_bdry_surf[i](0) - origin_pt(0)));
557  interp_x_data.push_back(ref_inner_bdry_surf[i](0));
558  interp_y_data.push_back(ref_inner_bdry_surf[i](1));
559  if (interp_azi_data.size() > 1)
560  if (interp_azi_data.back() < interp_azi_data[interp_azi_data.size() - 2])
561  interp_azi_data.back() += 2.0 * M_PI;
562  }
563  }
564  const Real interp_x0 = interp_x_data.front();
565  const Real interp_xt = interp_x_data.back();
566  const Real interp_y0 = interp_y_data.front();
567  const Real interp_yt = interp_y_data.back();
568  const Real incept_azi0 = interp_azi_data.front();
569  const Real incept_azit = interp_azi_data.back();
570 
571  if (MooseUtils::absoluteFuzzyGreaterThan(incept_azi0, -M_PI))
572  {
573  interp_azi_data.insert(interp_azi_data.begin(), incept_azit - 2.0 * M_PI);
574  interp_x_data.insert(interp_x_data.begin(), interp_xt);
575  interp_y_data.insert(interp_y_data.begin(), interp_yt);
576  }
577  else
578  interp_azi_data.front() = -M_PI;
579  if (MooseUtils::absoluteFuzzyLessThan(incept_azit, M_PI))
580  {
581  interp_azi_data.push_back(incept_azi0 + 2 * M_PI);
582  interp_x_data.push_back(interp_x0);
583  interp_y_data.push_back(interp_y0);
584  }
585  else
586  interp_azi_data.back() = M_PI;
587 
588  // Establish interpolation
589  linterp_x = std::make_unique<LinearInterpolation>(interp_azi_data, interp_x_data);
590  linterp_y = std::make_unique<LinearInterpolation>(interp_azi_data, interp_y_data);
591  // Loop to handle inner boundary layer
592  for (const auto i : make_range(input_ext_node_num))
593  {
594  // Outside point of the inner boundary layer
595  // Using interpolation, the azimuthal angles do not need to be changed.
596  const Point inner_boundary_shift = Point(linterp_x->sample(azi_array[i] / 180.0 * M_PI),
597  linterp_y->sample(azi_array[i] / 180.0 * M_PI),
598  origin_pt(2)) -
599  points_array[0][i];
600  for (const auto j : make_range(uint(1), _peripheral_inner_boundary_layer_params.intervals + 1))
601  points_array[j][i] =
602  points_array[0][i] + inner_boundary_shift * inner_peripheral_bias_terms[j - 1];
603  }
604 }
void addRequiredRangeCheckedParam(const std::string &name, const std::string &parsed_function, const std::string &doc_string)
CTSub CT_OPERATOR_BINARY CTMul CTCompareLess CTCompareGreater CTCompareEqual _arg template * sin(_arg) *_arg.template D< dtag >()) CT_SIMPLE_UNARY_FUNCTION(tan
const SubdomainName _peripheral_ring_block_name
Subdomain name of the added peripheral region.
virtual const char * what() const
T & setMeshProperty(const std::string &data_name, Args &&... args)
const BoundaryName _input_mesh_external_boundary
Name of the external boundary of the input mesh.
std::unique_ptr< ReplicatedMesh > buildReplicatedMesh(unsigned int dim=libMesh::invalid_uint)
bool hasBoundaryName(const MeshBase &input_mesh, const BoundaryName &name)
void paramError(const std::string &param, Args... args) const
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
std::unique_ptr< MeshBase > & _input
Reference to input mesh pointer.
static InputParameters validParams()
This PeripheralRingMeshGenerator object adds a circular peripheral region to the input mesh...
const subdomain_id_type _peripheral_ring_block_id
Subdomain ID of the added peripheral region.
const BoundaryName _external_boundary_name
Name of the new external boundary.
MeshBase & mesh
std::unique_ptr< T_DEST, T_DELETER > dynamic_pointer_cast(std::unique_ptr< T_SRC, T_DELETER > &src)
void changeBoundaryId(const boundary_id_type old_id, const boundary_id_type new_id, bool delete_prev)
void addRequiredParam(const std::string &name, const std::string &doc_string)
const bool _force_input_centroid_as_center
Whether to enforce use of the centroid position of the input mesh as the center of the peripheral rin...
BoundaryID getBoundaryID(const BoundaryName &boundary_name, const MeshBase &mesh)
const Real _peripheral_ring_radius
Radius of the peripheral region&#39;s outer circular boundary.
std::unique_ptr< MeshBase > generate() override
PeripheralRingMeshGenerator(const InputParameters &parameters)
std::vector< std::vector< Real > > biasTermsCalculator(const std::vector< Real > radial_biases, const std::vector< unsigned int > intervals, const multiBdryLayerParams inner_boundary_layer_params, const multiBdryLayerParams outer_boundary_layer_params) const
Creates bias terms for multiple blocks.
std::size_t euclideanMod(T1 dividend, T2 divisor)
static InputParameters validParams()
singleBdryLayerParams _peripheral_inner_boundary_layer_params
Width, fraction, radiation sectors and growth factor of the inner boundary layer of the peripheral re...
int8_t boundary_id_type
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)
boundary_id_type _input_mesh_external_bid
ID of the external boundary of the input mesh.
Real _peripheral_radial_bias
Bias value used to induce biasing to radial meshing in peripheral ring region.
void innerBdryLayerNodesDefiner(const unsigned int input_ext_node_num, const std::vector< Real > input_bdry_angles, const std::vector< Point > ref_inner_bdry_surf, const std::vector< Real > inner_peripheral_bias_terms, const std::vector< Real > azi_array, const Point origin_pt, std::vector< std::vector< Point >> &points_array) const
Define node positions of the inner boundary layer that is conformal to the input mesh&#39;s external boun...
const boundary_id_type _external_boundary_id
ID of the new external boundary.
const bool _preserve_volumes
Volume preserving function is optional.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
EDGE2
auto norm(const T &a)
A base class that contains common members for Reactor module mesh generators.
singleBdryLayerParams _peripheral_outer_boundary_layer_params
Width, fraction, radiation sectors and growth factor of the outer boundary layer of the peripheral re...
IntRange< T > make_range(T beg, T end)
void addClassDescription(const std::string &doc_string)
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
bool isParamValid(const std::string &name) const
Point meshCentroidCalculator(const MeshBase &mesh)
Real radiusCorrectionFactor(const std::vector< Real > &azimuthal_list, const bool full_circle=true, const unsigned int order=1, const bool is_first_value_vertex=true)
Makes radial correction to preserve ring area.
const MeshGeneratorName _input_name
Name of the mesh generator to get the input mesh.
bool isExternalBoundary(MeshBase &mesh, const boundary_id_type bid)
EDGE3
void ErrorVector unsigned int
auto index_range(const T &sizable)
const unsigned int _peripheral_layer_num
Number of layers of elements of the peripheral region in radial direction.
uint8_t dof_id_type
registerMooseObject("ReactorApp", PeripheralRingMeshGenerator)
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)