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  if (*(input_mesh->elem_dimensions().begin()) != 2 ||
159  *(input_mesh->elem_dimensions().rbegin()) != 2)
160  paramError("input", "Only 2D meshes are supported.");
161 
165  paramError("input_mesh_external_boundary",
166  "External boundary does not exist in the input mesh");
167  // We check the element types of input mesh's external boundary here.
168  // We support both linear and quadratic sides (i.e., EDGE2 and EDGE3), but we cannot support mixed
169  // sides
170  auto side_list_tmp = input_mesh->get_boundary_info().build_side_list();
171  bool linear_side = false;
172  bool quadratic_side = false;
173  for (const auto & side : side_list_tmp)
174  {
175  if (std::get<2>(side) == _input_mesh_external_bid)
176  {
177  const auto etype = input_mesh->elem_ptr(std::get<0>(side))->side_type(std::get<1>(side));
178  if (etype == EDGE2)
179  linear_side = true;
180  else if (etype == EDGE3)
181  quadratic_side = true;
182  else
183  paramError("input", "Input contains elements that are not supported by this generator.");
184  }
185  }
186  if (linear_side && quadratic_side)
187  paramError("input", "Input contains mixed element types on the external boundary.");
188  const unsigned int order = linear_side ? 1 : 2;
189  if (order == 2)
190  {
198  }
199 
200  // Move the centroid of the mesh to (0, 0, 0) if input centroid is enforced to be the ring center.
201  const Point input_mesh_centroid = MooseMeshUtils::meshCentroidCalculator(*input_mesh);
203  MeshTools::Modification::translate(
204  *input_mesh, -input_mesh_centroid(0), -input_mesh_centroid(1), -input_mesh_centroid(2));
205 
206  // Use CoM of the input mesh as its origin for azimuthal calculation
207  const Point origin_pt =
208  _force_input_centroid_as_center ? Point(0.0, 0.0, 0.0) : input_mesh_centroid;
209  // Vessel for containing maximum radius of the boundary nodes
210  Real max_input_mesh_node_radius;
211 
212  // Calculate biasing terms
213  // For 2nd order mesh, as we need the mid points, the bias factor is square rooted.
214  const auto main_peripheral_bias_terms =
216  const auto inner_peripheral_bias_terms =
219  // It is easier to create outer boundary layer inversely (inwards). Thus, 1.0 / bias is used here.
220  // However, the input parameter definition is not affected.
221  const auto outer_peripheral_bias_terms =
224 
225  const unsigned int total_peripheral_layer_num =
228 
229  // Collect the external boundary nodes of the input mesh using the utility function
230  std::vector<dof_id_type> boundary_ordered_node_list;
231  try
232  {
234  max_input_mesh_node_radius,
235  boundary_ordered_node_list,
236  origin_pt,
238  }
239  catch (MooseException & e)
240  {
241  if (((std::string)e.what()).compare("The node list provided has more than one segments.") == 0)
242  paramError("input_mesh_external_boundary",
243  "This mesh generator does not work for the provided external boundary as it has "
244  "more than one segments.");
245  else
246  paramError("input_mesh_external_boundary", e.what());
247  }
248 
249  if (max_input_mesh_node_radius >= _peripheral_ring_radius)
250  paramError(
251  "peripheral_ring_radius",
252  "The peripheral ring to be generated must be large enough to cover the entire input mesh.");
254  paramError("input_mesh_external_boundary",
255  "The boundary provided is not an external boundary.");
256 
257  std::vector<Point> input_ext_bd_pts;
258  std::vector<Point> output_ext_bd_pts;
259  std::vector<std::tuple<Real, Point, Point>> azi_points;
260  std::vector<Real> azi_array;
261  Real tmp_azi(0.0);
262  auto mesh = buildReplicatedMesh(2);
263 
264  // Node counter for the external boundary
265  unsigned int input_ext_node_num = 0;
266 
267  // As the node list collected before is a simple closed loop, the last node is the same as the
268  // first node. We remove it here.
269  boundary_ordered_node_list.pop_back();
270  // Loop over all the boundary nodes
271  // For quadratic sides, the middle points are included automatically
272  for (const auto i : index_range(boundary_ordered_node_list))
273  {
274  input_ext_node_num++;
275  // Define nodes on the inner and outer boundaries of the peripheral region.
276  input_ext_bd_pts.push_back(input_mesh->point(boundary_ordered_node_list[i]));
277  tmp_azi =
278  atan2(input_ext_bd_pts.back()(1) - origin_pt(1), input_ext_bd_pts.back()(0) - origin_pt(0));
279  output_ext_bd_pts.push_back(Point(_peripheral_ring_radius * std::cos(tmp_azi),
280  _peripheral_ring_radius * std::sin(tmp_azi),
281  origin_pt(2)));
282  // a vector of tuples using azimuthal angle as the key to facilitate sorting
283  azi_points.push_back(
284  std::make_tuple(tmp_azi, input_ext_bd_pts.back(), output_ext_bd_pts.back()));
285  // a simple vector of azimuthal angles for radius correction purposes (in degree)
286  azi_array.push_back(tmp_azi / M_PI * 180.0);
287  }
288  // Only for quadratic sides, if the index of the first node after sorting is even, it is a vertex
289  // node; otherwise, it is a midpoint node.
290  const bool is_first_node_vertex =
291  order == 1 ||
292  !(std::distance(azi_array.begin(), std::min_element(azi_array.begin(), azi_array.end())) % 2);
293  std::sort(azi_points.begin(), azi_points.end());
294  std::sort(azi_array.begin(), azi_array.end());
295 
296  // Angles defined by three neighboring nodes on input mesh's external boundary
297  std::vector<Real> input_bdry_angles;
298  // Normal directions of input boundary nodes
299  std::vector<Point> input_bdry_nd;
300  for (const auto i : index_range(azi_points))
301  {
302  Point p1, p2;
303  const Point pn = Point(0.0, 0.0, 1.0);
304  if (i == 0)
305  {
306  p1 = std::get<1>(azi_points[i + 1]) - std::get<1>(azi_points[i]);
307  p2 = std::get<1>(azi_points.back()) - std::get<1>(azi_points[i]);
308  }
309  else if (i == azi_points.size() - 1)
310  {
311  p1 = std::get<1>(azi_points.front()) - std::get<1>(azi_points.back());
312  p2 = std::get<1>(azi_points[i - 1]) - std::get<1>(azi_points.back());
313  }
314  else
315  {
316  p1 = std::get<1>(azi_points[i + 1]) - std::get<1>(azi_points[i]);
317  p2 = std::get<1>(azi_points[i - 1]) - std::get<1>(azi_points[i]);
318  }
319  // Use cross point to get perpendicular direction
320  const Point p1n = (p1.cross(pn)).unit();
321  const Point p2n = -(p2.cross(pn)).unit();
322  Real tmp = p1 * p2 / p1.norm() / p2.norm();
323  // Make sure acos() gets valid input
324  tmp = tmp > 1.0 ? 1.0 : (tmp < -1.0 ? -1.0 : tmp);
325  input_bdry_angles.push_back(acos(tmp) / 2.0);
326  input_bdry_nd.push_back((p1n + p2n).unit());
327  }
328 
329  // 2D vector containing all the node positions of the peripheral region
330  std::vector<std::vector<Point>> points_array(total_peripheral_layer_num + 1,
331  std::vector<Point>(input_ext_node_num));
332  // 2D vector containing all the node ids of the peripheral region
333  std::vector<std::vector<dof_id_type>> node_id_array(total_peripheral_layer_num + 1,
334  std::vector<dof_id_type>(input_ext_node_num));
335  // Reference outmost layer of inner boundary layer
336  std::vector<Point> ref_inner_bdry_surf;
337  // First loop
338  for (const auto i : make_range(input_ext_node_num))
339  {
340  // Inner boundary nodes of the peripheral region
341  points_array[0][i] = std::get<1>(azi_points[i]);
342  // Define outer layer of inner boundary layer
344  {
345  // Outside point of the inner boundary layer
346  const Point ref_inner_boundary_shift =
347  (_peripheral_inner_boundary_layer_params.width / sin(input_bdry_angles[i])) *
348  input_bdry_nd[i];
349  ref_inner_bdry_surf.push_back(points_array[0][i] + ref_inner_boundary_shift);
350  }
351  }
352 
354  innerBdryLayerNodesDefiner(input_ext_node_num,
355  input_bdry_angles,
356  ref_inner_bdry_surf,
357  inner_peripheral_bias_terms,
358  azi_array,
359  origin_pt,
360  points_array);
361  const Real correction_factor = _preserve_volumes
363  azi_array, true, order, is_first_node_vertex)
364  : 1.0;
365  // Loop to handle outer boundary layer and main region
366  for (const auto i : make_range(input_ext_node_num))
367  {
368  // Outer boundary nodes of the peripheral region
369  points_array[total_peripheral_layer_num][i] = std::get<2>(azi_points[i]) * correction_factor;
370  // Outer boundary layer points
372  {
373  // Inner point of the outer boundary layer
374  const Point outer_boundary_shift =
375  -Point(std::cos(std::get<0>(azi_points[i])), std::sin(std::get<0>(azi_points[i])), 0.0) *
377  for (const auto j :
379  points_array[total_peripheral_layer_num - j][i] =
380  points_array[total_peripheral_layer_num][i] +
381  outer_boundary_shift * outer_peripheral_bias_terms[j - 1];
382  }
383  // Use interpolation to get main region
385  (points_array[_peripheral_inner_boundary_layer_params.intervals][i] - origin_pt).norm(),
387  _peripheral_layer_num * order][i] -
388  origin_pt)
389  .norm()))
390  {
391  paramError("peripheral_inner_boundary_layer_width",
392  "The summation of peripheral_inner_boundary_layer_width and "
393  "peripheral_outer_boundary_layer_width must be smaller than the thickness of "
394  "peripheral ring region.");
395  }
396 
397  for (const auto j : make_range(uint(1), _peripheral_layer_num * order))
400  (1.0 - main_peripheral_bias_terms[j - 1]) +
402  _peripheral_layer_num * order][i] *
403  main_peripheral_bias_terms[j - 1];
404  }
405  unsigned int num_total_nodes = (total_peripheral_layer_num + 1) * input_ext_node_num;
406  std::vector<Node *> nodes(num_total_nodes); // reserve nodes pointers
407  dof_id_type node_id = 0;
408 
409  // Add nodes to the new mesh
410  for (const auto i : make_range(total_peripheral_layer_num + 1))
411  for (const auto j : make_range(input_ext_node_num))
412  {
413  nodes[node_id] = mesh->add_point(points_array[i][j], node_id);
414  node_id_array[i][j] = node_id;
415  node_id++;
416  }
417  // Add elements to the new mesh
418  BoundaryInfo & boundary_info = mesh->get_boundary_info();
419  const unsigned int index_shift = is_first_node_vertex ? 0 : 1;
420  for (const auto i : make_range(total_peripheral_layer_num / order))
421  for (const auto j : make_range(input_ext_node_num / order))
422  {
423  std::unique_ptr<Elem> new_elem;
424  new_elem = std::make_unique<Quad4>();
425  if (order == 2)
426  {
427  new_elem = std::make_unique<Quad9>();
428  new_elem->set_node(4, nodes[node_id_array[i * order + 1][j * order + index_shift]]);
429  new_elem->set_node(5,
430  nodes[node_id_array[(i + 1) * order][(j * order + 1 + index_shift) %
431  input_ext_node_num]]);
432  new_elem->set_node(6,
433  nodes[node_id_array[i * order + 1][((j + 1) * order + index_shift) %
434  input_ext_node_num]]);
435  new_elem->set_node(
436  7, nodes[node_id_array[i * order][(j * order + 1 + index_shift) % input_ext_node_num]]);
437  new_elem->set_node(8,
438  nodes[node_id_array[i * order + 1][(j * order + 1 + index_shift) %
439  input_ext_node_num]]);
440  }
441  new_elem->set_node(0, nodes[node_id_array[i * order][j * order + index_shift]]);
442  new_elem->set_node(1, nodes[node_id_array[(i + 1) * order][j * order + index_shift]]);
443  new_elem->set_node(2,
444  nodes[node_id_array[(i + 1) * order][((j + 1) * order + index_shift) %
445  input_ext_node_num]]);
446  new_elem->set_node(
447  3, nodes[node_id_array[i * order][((j + 1) * order + index_shift) % input_ext_node_num]]);
448  new_elem->subdomain_id() = _peripheral_ring_block_id;
449 
450  Elem * added_elem = mesh->add_elem(std::move(new_elem));
451 
452  if (i == 0)
453  boundary_info.add_side(added_elem, 3, OUTER_SIDESET_ID_ALT);
454  if (i == total_peripheral_layer_num / order - 1)
455  boundary_info.add_side(added_elem, 1, OUTER_SIDESET_ID);
456  }
457 
458  // This would make sure that the boundary OUTER_SIDESET_ID is deleted after stitching.
461  mesh->prepare_for_use();
462  // Use input_mesh here to retain the subdomain name map
463  input_mesh->stitch_meshes(*mesh, OUTER_SIDESET_ID, OUTER_SIDESET_ID_ALT, TOLERANCE, true, false);
464 
465  // Assign subdomain name to the new block if applicable
466  if (isParamValid("peripheral_ring_block_name"))
467  input_mesh->subdomain_name(_peripheral_ring_block_id) = _peripheral_ring_block_name;
468  // Assign customized external boundary id
469  if (_external_boundary_id > 0)
471  // Assign customized external boundary name
472  if (!_external_boundary_name.empty())
473  {
474  input_mesh->get_boundary_info().sideset_name(
477  input_mesh->get_boundary_info().nodeset_name(
480  }
481 
482  _input->set_isnt_prepared();
483  return dynamic_pointer_cast<MeshBase>(_input);
484 }
485 
486 void
488  const unsigned int input_ext_node_num,
489  const std::vector<Real> input_bdry_angles,
490  const std::vector<Point> ref_inner_bdry_surf,
491  const std::vector<Real> inner_peripheral_bias_terms,
492  const std::vector<Real> azi_array,
493  const Point origin_pt,
494  std::vector<std::vector<Point>> & points_array) const
495 {
496  // Check if any azimuthal angles are messed after inner boundary layer addition
497  std::vector<bool> delete_mark(input_ext_node_num, false);
498  std::vector<Real> interp_azi_data, interp_x_data, interp_y_data;
499  std::unique_ptr<LinearInterpolation> linterp_x, linterp_y;
500  // Mark the to-be-deleted elements
501  for (const auto i : make_range(input_ext_node_num))
502  {
503  // For a zig-zag external boundary, when we add a conformal layer onto it by translating the
504  // nodes in the surface normal direction, it is possible that the azimuthal order is flipped.
505  // As shown below, o's are the original boundary nodes, and *'s are the nodes after
506  // translation by the boundary layer thickness.
507  // * *
508  // | | | /
509  // o o-------/--*
510  // | | / |
511  // | outside --> | / |
512  // | | / |
513  // o--------o-- o-------o--
514  // To mitigate this flipping issue, we check the node flipping here using the cross product of
515  // neighboring node-to-origin vectors. Flipped nodes are marked and excluded during the
516  // follow-up interpolation.
517  if (!MooseUtils::absoluteFuzzyEqual(input_bdry_angles[i], M_PI / 2.0, 1.0e-4))
518  {
519  unsigned int index_shift = 1;
520  bool minus_shift_flag = true;
521  bool plus_shift_flag = true;
522  // Need to check both directions for multiple shifts
523  // Half of the external boundary nodes are used as an upper limit to avert infinite loops
524  while (index_shift < input_ext_node_num / 2 && (minus_shift_flag || plus_shift_flag))
525  {
526  if (((ref_inner_bdry_surf[MathUtils::euclideanMod(((int)i - (int)index_shift),
527  (int)input_ext_node_num)] -
528  origin_pt)
529  .cross(ref_inner_bdry_surf[i] - origin_pt))(2) <= 0.0 &&
530  minus_shift_flag)
531  delete_mark[MathUtils::euclideanMod(((int)i - (int)index_shift),
532  (int)input_ext_node_num)] = true;
533  else
534  minus_shift_flag = false;
535  if (((ref_inner_bdry_surf[(i + index_shift) % input_ext_node_num] - origin_pt)
536  .cross(ref_inner_bdry_surf[i] - origin_pt))(2) >= 0.0 &&
537  plus_shift_flag)
538  delete_mark[(i + index_shift) % input_ext_node_num] = true;
539  else
540  plus_shift_flag = false;
541  index_shift++;
542  }
543  }
544  }
545  // Create vectors for interpolation
546  // Due to the flip issue, linear interpolation is used here to mark the location of the boundary
547  // layer's outer boundary.
548  for (const auto i : make_range(input_ext_node_num))
549  {
550  if (!delete_mark[i])
551  {
552  interp_azi_data.push_back(atan2(ref_inner_bdry_surf[i](1) - origin_pt(1),
553  ref_inner_bdry_surf[i](0) - origin_pt(0)));
554  interp_x_data.push_back(ref_inner_bdry_surf[i](0));
555  interp_y_data.push_back(ref_inner_bdry_surf[i](1));
556  if (interp_azi_data.size() > 1)
557  if (interp_azi_data.back() < interp_azi_data[interp_azi_data.size() - 2])
558  interp_azi_data.back() += 2.0 * M_PI;
559  }
560  }
561  const Real interp_x0 = interp_x_data.front();
562  const Real interp_xt = interp_x_data.back();
563  const Real interp_y0 = interp_y_data.front();
564  const Real interp_yt = interp_y_data.back();
565  const Real incept_azi0 = interp_azi_data.front();
566  const Real incept_azit = interp_azi_data.back();
567 
568  if (MooseUtils::absoluteFuzzyGreaterThan(incept_azi0, -M_PI))
569  {
570  interp_azi_data.insert(interp_azi_data.begin(), incept_azit - 2.0 * M_PI);
571  interp_x_data.insert(interp_x_data.begin(), interp_xt);
572  interp_y_data.insert(interp_y_data.begin(), interp_yt);
573  }
574  else
575  interp_azi_data.front() = -M_PI;
576  if (MooseUtils::absoluteFuzzyLessThan(incept_azit, M_PI))
577  {
578  interp_azi_data.push_back(incept_azi0 + 2 * M_PI);
579  interp_x_data.push_back(interp_x0);
580  interp_y_data.push_back(interp_y0);
581  }
582  else
583  interp_azi_data.back() = M_PI;
584 
585  // Establish interpolation
586  linterp_x = std::make_unique<LinearInterpolation>(interp_azi_data, interp_x_data);
587  linterp_y = std::make_unique<LinearInterpolation>(interp_azi_data, interp_y_data);
588  // Loop to handle inner boundary layer
589  for (const auto i : make_range(input_ext_node_num))
590  {
591  // Outside point of the inner boundary layer
592  // Using interpolation, the azimuthal angles do not need to be changed.
593  const Point inner_boundary_shift = Point(linterp_x->sample(azi_array[i] / 180.0 * M_PI),
594  linterp_y->sample(azi_array[i] / 180.0 * M_PI),
595  origin_pt(2)) -
596  points_array[0][i];
597  for (const auto j : make_range(uint(1), _peripheral_inner_boundary_layer_params.intervals + 1))
598  points_array[j][i] =
599  points_array[0][i] + inner_boundary_shift * inner_peripheral_bias_terms[j - 1];
600  }
601 }
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)
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
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 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...
bool isParamValid(const std::string &name) const
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.
bool absoluteFuzzyLessThan(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
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...
void paramError(const std::string &param, Args... args) const
auto norm(const T &a) -> decltype(std::abs(a))
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
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)
bool absoluteFuzzyGreaterEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
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)
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)
bool absoluteFuzzyGreaterThan(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
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)