https://mooseframework.inl.gov
AzimuthalBlockSplitGenerator.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"
13 
14 // C++ includes
15 #include <cmath> // provides round, not std::round (see http://www.cplusplus.com/reference/cmath/round/)
16 
18 
21 {
23  params.addRequiredParam<MeshGeneratorName>("input", "The input mesh to be modified.");
24  params.addRequiredParam<std::vector<SubdomainName>>(
25  "old_blocks", "The list of blocks in the input mesh that need to be modified.");
26  params.addRequiredParam<std::vector<subdomain_id_type>>(
27  "new_block_ids", "The block IDs to be used for the new selected azimuthal angle blocks.");
28  params.addParam<std::vector<SubdomainName>>(
29  "new_block_names",
30  {},
31  "The optional block names to be used for the new selected azimulathal angle blocks.");
32  params.addParam<bool>("preserve_volumes",
33  true,
34  "Volume of concentric circles can be preserved using this function.");
35  params.addRequiredRangeCheckedParam<Real>("start_angle",
36  "start_angle>=0.0 & start_angle<360.0",
37  "Starting azimuthal angle of the new block.");
38  params.addRequiredRangeCheckedParam<Real>("angle_range",
39  "angle_range>0.0 & angle_range<=180.0",
40  "Azimuthal angle range of the new block.");
41  params.addClassDescription(
42  "This AzimuthalBlockSplitGenerator object takes in a polygon/hexagon concentric circle mesh "
43  "and renames blocks on a user-defined azimuthal segment / wedge of the mesh.");
44 
45  return params;
46 }
47 
49  : PolygonMeshGeneratorBase(parameters),
50  _input_name(getParam<MeshGeneratorName>("input")),
51  _new_block_ids(getParam<std::vector<subdomain_id_type>>("new_block_ids")),
52  _new_block_names(getParam<std::vector<SubdomainName>>("new_block_names")),
53  _preserve_volumes(getParam<bool>("preserve_volumes")),
54  _start_angle(getParam<Real>("start_angle") + 90.0),
55  _angle_range(getParam<Real>("angle_range")),
56  _azimuthal_angle_meta(
57  declareMeshProperty<std::vector<Real>>("azimuthal_angle_meta", std::vector<Real>())),
58  _input(getMeshByName(_input_name))
59 {
60  declareMeshProperty<Real>("pattern_pitch_meta", 0.0);
61  declareMeshProperty<bool>("is_control_drum_meta", true);
62  if (!_new_block_names.empty() && _new_block_names.size() != _new_block_ids.size())
63  paramError("new_block_names",
64  "This parameter, if provided, must have the same size as new_block_ids.");
66  getMeshProperty<std::vector<unsigned int>>("num_sectors_per_side_meta", _input_name);
67  _start_angle = _start_angle >= 360.0 ? _start_angle - 360.0 : _start_angle;
68  _end_angle = (_start_angle + _angle_range >= 360.0) ? (_start_angle + _angle_range - 360.0)
70 }
71 
72 std::unique_ptr<MeshBase>
74 {
75  auto replicated_mesh_ptr = dynamic_cast<ReplicatedMesh *>(_input.get());
76  if (!replicated_mesh_ptr)
77  paramError("input", "Input is not a replicated mesh, which is required");
78 
79  ReplicatedMesh & mesh = *replicated_mesh_ptr;
80 
81  // We'll be querying the mesh for subdomain ids
82  if (!mesh.preparation().has_cached_elem_data)
83  mesh.cache_elem_data();
84 
85  // Check the order of the input mesh's elements
86  // Meanwhile, record the vertex average of each element for future comparison
87  unsigned short order = 0;
88  std::map<dof_id_type, Point> vertex_avgs;
89  for (const auto & elem : mesh.element_ptr_range())
90  {
91  switch (elem->type())
92  {
93  case TRI3:
94  case QUAD4:
95  if (order == 2)
96  paramError("input", "This mesh contains elements of different orders.");
97  order = 1;
98  break;
99  case TRI6:
100  case TRI7:
101  case QUAD8:
102  case QUAD9:
103  if (order == 1)
104  paramError("input", "This mesh contains elements of different orders.");
105  order = 2;
106  break;
107  default:
108  paramError("input", "This mesh contains elements of unsupported type.");
109  }
110  vertex_avgs[elem->id()] = elem->vertex_average();
111  }
112 
114  MooseMeshUtils::getSubdomainIDs(mesh, getParam<std::vector<SubdomainName>>("old_blocks"));
115  if (_old_block_ids.size() != _new_block_ids.size())
116  paramError("new_block_ids", "This parameter must have the same size as old_blocks.");
117 
118  // Check that the block ids/names exist in the mesh
119  std::set<SubdomainID> mesh_blocks;
120  mesh.subdomain_ids(mesh_blocks);
121 
122  for (unsigned int i = 0; i < _old_block_ids.size(); i++)
123  if (_old_block_ids[i] == Moose::INVALID_BLOCK_ID || !mesh_blocks.count(_old_block_ids[i]))
124  paramError("old_blocks",
125  "This parameter contains blocks that do not exist in the input mesh. Error "
126  "triggered by block: " +
127  getParam<std::vector<SubdomainName>>("old_blocks")[i]);
128 
129  if (std::find(_old_block_ids.begin(),
130  _old_block_ids.end(),
131  getMeshProperty<subdomain_id_type>("quad_center_block_id", _input_name)) !=
132  _old_block_ids.end())
133  paramError(
134  "old_blocks",
135  "This parameter contains a block that involves center quad elements, azimuthal splitting "
136  "is currently not supported in this case.");
137 
138  MeshTools::Modification::rotate(mesh, 90.0, 0.0, 0.0);
140  std::vector<Real> azimuthal_angles_vtx;
141  const bool is_first_value_vertex =
142  order == 1 ? true : MooseUtils::absoluteFuzzyEqual(_azimuthal_angle_meta[0], -180.0);
143  if (order == 1)
144  azimuthal_angles_vtx = _azimuthal_angle_meta;
145  else
146  for (const auto & i : make_range(_azimuthal_angle_meta.size()))
147  if (i % 2 != is_first_value_vertex)
148  azimuthal_angles_vtx.push_back(_azimuthal_angle_meta[i]);
149 
150  // So that this class works for both derived classes of PolygonMeshGeneratorBase
151  auto pattern_pitch_meta = std::max(getMeshProperty<Real>("pitch_meta", _input_name),
152  getMeshProperty<Real>("pattern_pitch_meta", _input_name));
153  setMeshProperty("pattern_pitch_meta", pattern_pitch_meta);
154 
155  Real radiusCorrectionFactor_original =
157  _azimuthal_angle_meta, true, order, is_first_value_vertex)
158  : 1.0;
159 
160  const Real azi_tol = 1.0E-6;
161  const Real rad_tol = 1.0e-6;
162  const Real side_angular_range = 360.0 / (Real)_num_sectors_per_side_meta.size();
163  const Real side_angular_shift =
164  _num_sectors_per_side_meta.size() % 2 == 0 ? 0.0 : side_angular_range / 2.0;
165 
166  _start_angle = _start_angle > 180.0 ? _start_angle - 360.0 : _start_angle;
167  _end_angle = _end_angle > 180.0 ? _end_angle - 360.0 : _end_angle;
168 
169  Real original_start_down;
170  std::vector<Real>::iterator original_start_down_it;
171  Real original_start_up;
172  std::vector<Real>::iterator original_start_up_it;
173 
174  // Identify the mesh azimuthal angles of the elements that need to be modified for the starting
175  // edge of the absorber region.
177  original_start_down,
178  original_start_down_it,
179  original_start_up,
180  original_start_up_it,
181  azimuthal_angles_vtx);
182 
183  Real original_end_down;
184  std::vector<Real>::iterator original_end_down_it;
185  Real original_end_up;
186  std::vector<Real>::iterator original_end_up_it;
187 
188  // Identify the mesh azimuthal angles of the elements that need to be modified for the ending
189  // edge of the absorber region.
191  original_end_down,
192  original_end_down_it,
193  original_end_up,
194  original_end_up_it,
195  azimuthal_angles_vtx);
196 
197  Real azi_to_mod_start;
198  Real azi_to_keep_start;
199 
200  // For the two mesh azimuthal angles identified, determine which need to be moved to the starting
201  // edge position.
202  angleModifier(side_angular_shift,
203  side_angular_range,
204  azi_tol,
205  _start_angle,
206  original_start_down,
207  original_start_down_it,
208  original_start_up,
209  original_start_up_it,
210  azi_to_keep_start,
211  azi_to_mod_start);
212 
213  Real azi_to_mod_end;
214  Real azi_to_keep_end;
215 
216  // For the two mesh azimuthal angles identified, determine which need to be moved to the ending
217  // edge position.
218  angleModifier(side_angular_shift,
219  side_angular_range,
220  azi_tol,
221  _end_angle,
222  original_end_down,
223  original_end_down_it,
224  original_end_up,
225  original_end_up_it,
226  azi_to_keep_end,
227  azi_to_mod_end);
228 
229  if (azi_to_mod_end == azi_to_mod_start)
230  paramError("angle_range",
231  "The azimuthal intervals of the input mesh are too coarse for this parameter.");
232 
233  const auto & side_list = mesh.get_boundary_info().build_side_list();
234 
235  // See if the block that contains the external boundary is modified or not
236  bool external_block_change(false);
237  for (unsigned int i = 0; i < side_list.size(); i++)
238  {
239  if (std::get<2>(side_list[i]) == OUTER_SIDESET_ID)
240  {
241  dof_id_type block_id_tmp = mesh.elem_ref(std::get<0>(side_list[i])).subdomain_id();
242  if (std::find(_old_block_ids.begin(), _old_block_ids.end(), block_id_tmp) !=
243  _old_block_ids.end())
244  external_block_change = true;
245  }
246  }
247 
248  mesh.get_boundary_info().build_node_list_from_side_list();
249  std::vector<std::tuple<dof_id_type, boundary_id_type>> node_list =
250  mesh.get_boundary_info().build_node_list();
251 
252  std::vector<std::pair<Real, dof_id_type>> node_id_keep_start;
253  std::vector<std::pair<Real, dof_id_type>> node_id_mod_start;
254  std::vector<std::pair<Real, dof_id_type>> node_id_keep_end;
255  std::vector<std::pair<Real, dof_id_type>> node_id_mod_end;
256 
257  // Determine which nodes are involved in the elements that are intercepted by the starting/ending
258  // angles
259  Real max_quad_elem_rad(0.0);
260  if (mesh.elem_ref(0).n_vertices() == 4)
261  for (dof_id_type i = 0;
262  i < (_num_sectors_per_side_meta[0] / 2 + 1) * (_num_sectors_per_side_meta[0] / 2 + 1);
263  i++)
264  max_quad_elem_rad =
265  max_quad_elem_rad < mesh.node_ref(i).norm() ? mesh.node_ref(i).norm() : max_quad_elem_rad;
266  for (const auto & node_ptr : as_range(mesh.nodes_begin(), mesh.nodes_end()))
267  {
268  const Node & node = *node_ptr;
269  Real node_azi = atan2(node(1), node(0)) * 180.0 / M_PI;
270  Real node_rad = std::sqrt(node(0) * node(0) + node(1) * node(1));
271  if (node_rad > max_quad_elem_rad + rad_tol &&
272  (std::abs(node_azi - azi_to_mod_start) < azi_tol ||
273  std::abs(std::abs(node_azi - azi_to_mod_start) - 360.0) < azi_tol))
274  node_id_mod_start.push_back(std::make_pair(node_rad, node.id()));
275  if (node_rad > max_quad_elem_rad + rad_tol &&
276  (std::abs(node_azi - azi_to_keep_start) < azi_tol ||
277  std::abs(std::abs(node_azi - azi_to_keep_start) - 360.0) < azi_tol))
278  node_id_keep_start.push_back(std::make_pair(node_rad, node.id()));
279  if (node_rad > max_quad_elem_rad + rad_tol &&
280  (std::abs(node_azi - azi_to_mod_end) < azi_tol ||
281  std::abs(std::abs(node_azi - azi_to_mod_end) - 360.0) < azi_tol))
282  node_id_mod_end.push_back(std::make_pair(node_rad, node.id()));
283  if (node_rad > max_quad_elem_rad + rad_tol &&
284  (std::abs(node_azi - azi_to_keep_end) < azi_tol ||
285  std::abs(std::abs(node_azi - azi_to_keep_end) - 360.0) < azi_tol))
286  node_id_keep_end.push_back(std::make_pair(node_rad, node.id()));
287  }
288  // Sort the involved nodes using radius as the key; this facilitates the determination of circular
289  // regions nodes
290  std::sort(node_id_mod_start.begin(), node_id_mod_start.end());
291  std::sort(node_id_keep_start.begin(), node_id_keep_start.end());
292  std::sort(node_id_mod_end.begin(), node_id_mod_end.end());
293  std::sort(node_id_keep_end.begin(), node_id_keep_end.end());
294  std::vector<Real> circular_rad_list;
295  std::vector<Real> non_circular_rad_list;
296 
297  // Modify the nodes with the azimuthal angles identified before.
299  node_id_mod_start,
300  node_id_keep_start,
301  circular_rad_list,
302  non_circular_rad_list,
303  node_list,
304  _start_angle,
305  external_block_change,
306  rad_tol);
308  node_id_mod_end,
309  node_id_keep_end,
310  circular_rad_list,
311  non_circular_rad_list,
312  node_list,
313  _end_angle,
314  external_block_change,
315  rad_tol);
316 
317  const Real max_circular_radius =
318  *std::max_element(circular_rad_list.begin(), circular_rad_list.end());
319  const Real min_non_circular_radius =
320  *std::min_element(non_circular_rad_list.begin(), non_circular_rad_list.end());
321 
322  // Before re-correcting the radii, correct the mid-point of the elements that are altered
323  if (order == 2)
324  for (const auto & elem : mesh.element_ptr_range())
325  {
326  const Point & original_vertex_avg = vertex_avgs[elem->id()];
327  const Point & new_vertex_avg = elem->vertex_average();
328  if (MooseUtils::absoluteFuzzyGreaterThan((original_vertex_avg - new_vertex_avg).norm(), 0.0))
329  {
330  if (elem->type() == TRI6 || elem->type() == TRI7)
331  {
332  *elem->node_ptr(3) = midPointCorrector(
333  *elem->node_ptr(0), *elem->node_ptr(1), max_circular_radius, rad_tol);
334  *elem->node_ptr(4) = midPointCorrector(
335  *elem->node_ptr(1), *elem->node_ptr(2), max_circular_radius, rad_tol);
336  *elem->node_ptr(5) = midPointCorrector(
337  *elem->node_ptr(2), *elem->node_ptr(0), max_circular_radius, rad_tol);
338  if (elem->type() == TRI7)
339  *elem->node_ptr(6) = (*elem->node_ptr(0) + *elem->node_ptr(1) + *elem->node_ptr(2) +
340  *elem->node_ptr(3) + *elem->node_ptr(4) + *elem->node_ptr(5)) /
341  6.0;
342  }
343  else if (elem->type() == QUAD8 || elem->type() == QUAD9)
344  {
345  *elem->node_ptr(4) = midPointCorrector(
346  *elem->node_ptr(0), *elem->node_ptr(1), max_circular_radius, rad_tol);
347  *elem->node_ptr(5) = midPointCorrector(
348  *elem->node_ptr(1), *elem->node_ptr(2), max_circular_radius, rad_tol);
349  *elem->node_ptr(6) = midPointCorrector(
350  *elem->node_ptr(2), *elem->node_ptr(3), max_circular_radius, rad_tol);
351  *elem->node_ptr(7) = midPointCorrector(
352  *elem->node_ptr(3), *elem->node_ptr(0), max_circular_radius, rad_tol);
353  if (elem->type() == QUAD9)
354  *elem->node_ptr(8) = (*elem->node_ptr(0) + *elem->node_ptr(1) + *elem->node_ptr(2) +
355  *elem->node_ptr(3) + *elem->node_ptr(4) + *elem->node_ptr(5) +
356  *elem->node_ptr(6) + *elem->node_ptr(7)) /
357  8.0;
358  }
359  }
360  }
361 
362  std::vector<Real> new_azimuthal_angle;
363  for (const auto & node_ptr : as_range(mesh.nodes_begin(), mesh.nodes_end()))
364  {
365  const Node & node = *node_ptr;
366  if (MooseUtils::absoluteFuzzyEqual(
367  std::sqrt(node(0) * node(0) + node(1) * node(1)), max_circular_radius, rad_tol))
368  {
369  Real node_azi = atan2(node(1), node(0)) * 180.0 / M_PI;
370  new_azimuthal_angle.push_back(node_azi);
371  }
372  }
373  std::sort(new_azimuthal_angle.begin(), new_azimuthal_angle.end());
374  _azimuthal_angle_meta = new_azimuthal_angle;
375  const bool is_first_value_vertex_mod =
376  order == 1 ? true : MooseUtils::absoluteFuzzyEqual(_azimuthal_angle_meta[0], -180.0);
377 
378  Real radiusCorrectionFactor_mod =
380  _azimuthal_angle_meta, true, order, is_first_value_vertex_mod)
381  : 1.0;
382 
383  // Re-correct Radii
384  if (_preserve_volumes)
385  {
386  if (min_non_circular_radius <
387  max_circular_radius / radiusCorrectionFactor_original * radiusCorrectionFactor_mod)
388  mooseError("In AzimuthalBlockSplitGenerator ",
389  _name,
390  ": the circular region is overlapped with background region after correction.");
391  for (Node * node_ptr : as_range(mesh.nodes_begin(), mesh.nodes_end()))
392  {
393  Node & node = *node_ptr;
394  Real node_rad = std::sqrt(node(0) * node(0) + node(1) * node(1));
395  // Any nodes with radii smaller than the threshold belong to circular regions
396  if (node_rad > rad_tol && node_rad <= max_circular_radius + rad_tol)
397  {
398  const Real node_azi = atan2(node(1), node(0));
399  const Real node_rad_corr =
400  node_rad / radiusCorrectionFactor_original * radiusCorrectionFactor_mod;
401  node(0) = node_rad_corr * std::cos(node_azi);
402  node(1) = node_rad_corr * std::sin(node_azi);
403  }
404  }
405  }
406  // Assign New Block IDs
407  for (unsigned int block_id_index = 0; block_id_index < _old_block_ids.size(); block_id_index++)
408  for (auto & elem :
409  as_range(mesh.active_subdomain_elements_begin(_old_block_ids[block_id_index]),
410  mesh.active_subdomain_elements_end(_old_block_ids[block_id_index])))
411  {
412  auto p_cent = elem->true_centroid();
413  auto p_cent_azi = atan2(p_cent(1), p_cent(0)) * 180.0 / M_PI;
414  if (_start_angle < _end_angle && p_cent_azi >= _start_angle && p_cent_azi <= _end_angle)
415  elem->subdomain_id() = _new_block_ids[block_id_index];
416  else if (_start_angle > _end_angle &&
417  (p_cent_azi >= _start_angle || p_cent_azi <= _end_angle))
418  elem->subdomain_id() = _new_block_ids[block_id_index];
419  }
420 
421  // Assign new Block Names if applicable
422  for (unsigned int i = 0; i < _new_block_names.size(); i++)
423  mesh.subdomain_name(_new_block_ids[i]) = _new_block_names[i];
424 
425  MeshTools::Modification::rotate(mesh, -90.0, 0.0, 0.0);
426 
427  // Workaround - the point locator clear should be done in rotate()
428  mesh.clear_point_locator();
429 
430  // The rotate() could unset this (because spatial_dimension() can be
431  // changed) but let's unset to be sure our subdomain changes get
432  // recached.
433  mesh.unset_has_cached_elem_data();
434 
435  for (unsigned int i = 0; i < _azimuthal_angle_meta.size(); i++)
436  _azimuthal_angle_meta[i] = (_azimuthal_angle_meta[i] - 90.0 <= -180.0)
437  ? (_azimuthal_angle_meta[i] + 270.0)
438  : _azimuthal_angle_meta[i] - 90.0;
439  std::sort(_azimuthal_angle_meta.begin(), _azimuthal_angle_meta.end());
440 
441  return std::move(_input);
442 }
443 
444 void
446  Real & original_down,
447  std::vector<Real>::iterator & original_down_it,
448  Real & original_up,
449  std::vector<Real>::iterator & original_up_it,
450  std::vector<Real> & azimuthal_angles_vtx)
451 {
452 
453  auto term_up =
454  std::lower_bound(azimuthal_angles_vtx.begin(), azimuthal_angles_vtx.end(), terminal_angle);
455  if (term_up == azimuthal_angles_vtx.begin())
456  {
457  original_up = *term_up;
458  original_up_it = term_up;
459  original_down = -180.0;
460  original_down_it = azimuthal_angles_vtx.end() - 1;
461  }
462  else if (term_up == azimuthal_angles_vtx.end())
463  {
464  original_down = azimuthal_angles_vtx.back();
465  original_down_it = azimuthal_angles_vtx.end() - 1;
466  original_up = 180.0;
467  original_up_it = azimuthal_angles_vtx.begin();
468  }
469  else
470  {
471  original_down = *(term_up - 1);
472  original_down_it = term_up - 1;
473  original_up = *term_up;
474  original_up_it = term_up;
475  }
476 }
477 
478 void
479 AzimuthalBlockSplitGenerator::angleModifier(const Real & side_angular_shift,
480  const Real & side_angular_range,
481  const Real & azi_tol,
482  const Real & terminal_angle,
483  const Real & original_down,
484  std::vector<Real>::iterator & original_down_it,
485  const Real & original_up,
486  std::vector<Real>::iterator & original_up_it,
487  Real & azi_to_keep,
488  Real & azi_to_mod)
489 {
490  // Half interval is used to help determine which of the two identified azimuthal angles needs to
491  // be moved.
492  const Real half_interval = (original_up - original_down) / 2.0;
493  // In this case, the lower azimuthal angle matches a vertex position, while the target angle is
494  // not overlapped with the same vertex position. Thus the lower azimuthal angle is not moved
495  // anyway.
496  if (std::abs((original_down + side_angular_shift) / side_angular_range -
497  std::round((original_down + side_angular_shift) / side_angular_range)) < azi_tol &&
498  std::abs(original_down - terminal_angle) > azi_tol)
499  {
500  azi_to_keep = original_down;
501  azi_to_mod = original_up;
502  *original_up_it = terminal_angle;
503  }
504  // In this case, the upper azimuthal angle matches a vertex position, while the target angle is
505  // not overlapped with the same vertex position. Thus the upper azimuthal angle is not moved
506  // anyway.
507  else if (std::abs((original_up + side_angular_shift) / side_angular_range -
508  std::round((original_up + side_angular_shift) / side_angular_range)) <
509  azi_tol &&
510  std::abs(original_up - terminal_angle) > azi_tol)
511  {
512  azi_to_keep = original_up;
513  azi_to_mod = original_down;
514  *original_down_it = terminal_angle;
515  }
516  // Move upper azimuthal angle as it is closer to the target angle.
517  else if (terminal_angle - original_down > half_interval)
518  {
519  azi_to_keep = original_down;
520  azi_to_mod = original_up;
521  *original_up_it = terminal_angle;
522  }
523  // Move lower azimuthal angle as it is closer to the target angle.
524  else
525  {
526  azi_to_keep = original_up;
527  azi_to_mod = original_down;
528  *original_down_it = terminal_angle;
529  }
530 }
531 
532 void
534  ReplicatedMesh & mesh,
535  const std::vector<std::pair<Real, dof_id_type>> & node_id_mod,
536  const std::vector<std::pair<Real, dof_id_type>> & node_id_keep,
537  std::vector<Real> & circular_rad_list,
538  std::vector<Real> & non_circular_rad_list,
539  const std::vector<std::tuple<dof_id_type, boundary_id_type>> & node_list,
540  const Real & term_angle,
541  const bool & external_block_change,
542  const Real & rad_tol)
543 {
544  for (unsigned int i = 0; i < node_id_mod.size(); i++)
545  {
546  // Circular regions, radius is not altered
547  if (std::abs(node_id_mod[i].first - node_id_keep[i].first) < rad_tol)
548  {
549  Node & node_mod = mesh.node_ref(node_id_mod[i].second);
550  circular_rad_list.push_back(node_id_mod[i].first);
551  node_mod(0) = node_id_mod[i].first * std::cos(term_angle * M_PI / 180.0);
552  node_mod(1) = node_id_mod[i].first * std::sin(term_angle * M_PI / 180.0);
553  }
554  else
555  {
556  non_circular_rad_list.push_back(node_id_mod[i].first);
557  // For external boundary nodes, if the external block is not modified, the boundary nodes are
558  // not moved.
559  // Non-circular range, use intercept instead
560  if (std::find(node_list.begin(),
561  node_list.end(),
562  std::make_tuple(node_id_mod[i].second, OUTER_SIDESET_ID)) == node_list.end() ||
563  external_block_change)
564  {
565  Node & node_mod = mesh.node_ref(node_id_mod[i].second);
566  const Node & node_keep = mesh.node_ref(node_id_keep[i].second);
567  std::pair<Real, Real> pair_tmp = fourPointIntercept(
568  std::make_pair(node_mod(0), node_mod(1)),
569  std::make_pair(node_keep(0), node_keep(1)),
570  std::make_pair(0.0, 0.0),
571  std::make_pair(2.0 * node_id_mod[i].first * std::cos(term_angle * M_PI / 180.0),
572  2.0 * node_id_mod[i].first * std::sin(term_angle * M_PI / 180.0)));
573  node_mod(0) = pair_tmp.first;
574  node_mod(1) = pair_tmp.second;
575  }
576  }
577  }
578 }
579 
580 Point
582  const Point vertex_1,
583  const Real max_radius,
584  const Real rad_tol)
585 {
586  // Check if two vertices have the same radius
587  const Real r_vertex_0 = std::sqrt(vertex_0(0) * vertex_0(0) + vertex_0(1) * vertex_0(1));
588  const Real r_vertex_1 = std::sqrt(vertex_1(0) * vertex_1(0) + vertex_1(1) * vertex_1(1));
589  // If both vertices have the same radius and they are located in the circular region,
590  // their midpoint should have the same radius and has the average azimuthal angle of the two
591  // vertices.
592  if (std::abs(r_vertex_0 - r_vertex_1) < rad_tol && r_vertex_0 < max_radius + rad_tol)
593  return (vertex_0 + vertex_1).unit() * r_vertex_0;
594  else
595  return (vertex_0 + vertex_1) / 2.0;
596 }
std::pair< Real, Real > fourPointIntercept(const std::pair< Real, Real > &p1, const std::pair< Real, Real > &p2, const std::pair< Real, Real > &p3, const std::pair< Real, Real > &p4) const
Finds the center of a quadrilateral based on four vertices.
void addRequiredRangeCheckedParam(const std::string &name, const std::string &parsed_function, const std::string &doc_string)
T & setMeshProperty(const std::string &data_name, Args &&... args)
QUAD8
const std::string & _name
const Real _angle_range
Angular range of the modified azimuthal blocks.
void paramError(const std::string &param, Args... args) const
const T & getParam(const std::string &name) const
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
Point midPointCorrector(const Point vertex_0, const Point vertex_1, const Real max_radius, const Real rad_tol)
Calculate the corrected midpoint position of the element with vertices moved.
std::vector< unsigned int > _num_sectors_per_side_meta
MeshMetaData: number of mesh sectors of each polygon side.
MeshBase & mesh
std::vector< subdomain_id_type > getSubdomainIDs(const libMesh::MeshBase &mesh, const std::vector< SubdomainName > &subdomain_name)
std::vector< Real > azimuthalAnglesCollector(ReplicatedMesh &mesh, std::vector< Point > &boundary_points, const Real lower_azi=-30.0, const Real upper_azi=30.0, const unsigned int return_type=ANGLE_TANGENT, const unsigned int num_sides=6, const boundary_id_type bid=OUTER_SIDESET_ID, const bool calculate_origin=true, const Real input_origin_x=0.0, const Real input_origin_y=0.0, const Real tol=1.0E-10) const
Collects sorted azimuthal angles of the external boundary.
void addRequiredParam(const std::string &name, const std::string &doc_string)
std::vector< Real > & _azimuthal_angle_meta
MeshMetaData: vector of all nodes&#39; azimuthal angles.
TRI3
This AzimuthalBlockSplitGenerator object takes in a polygon/hexagon concentric circle mesh and rename...
QUAD4
const SubdomainID INVALID_BLOCK_ID
static InputParameters validParams()
std::unique_ptr< MeshBase > generate() override
std::unique_ptr< MeshBase > & _input
Reference to input mesh pointer.
TRI6
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
const bool _preserve_volumes
Whether volumes of ring regions need to be preserved.
const MeshGeneratorName _input_name
Input mesh to be modified.
void nodeModifier(ReplicatedMesh &mesh, const std::vector< std::pair< Real, dof_id_type >> &node_id_mod, const std::vector< std::pair< Real, dof_id_type >> &node_id_keep, std::vector< Real > &circular_rad_list, std::vector< Real > &non_circular_rad_list, const std::vector< std::tuple< dof_id_type, boundary_id_type >> &node_list, const Real &term_angle, const bool &external_block_change, const Real &rad_tol)
Modifies the nodes with the azimuthal angles modified in AzimuthalBlockSplitGenerator::angleModifier(...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const std::vector< SubdomainName > _new_block_names
Names of the new azimuthal blocks.
auto norm(const T &a)
registerMooseObject("ReactorApp", AzimuthalBlockSplitGenerator)
void angleIdentifier(const Real &terminal_angle, Real &original_down, std::vector< Real >::iterator &original_down_it, Real &original_up, std::vector< Real >::iterator &original_up_it, std::vector< Real > &azimuthal_angles_vtx)
Finds the azimuthal angles of the original mesh that need to be modified to match the edge locations ...
std::vector< subdomain_id_type > _old_block_ids
IDs to identify the input mesh blocks to be modified.
A base class that contains common members for Reactor module mesh generators.
IntRange< T > make_range(T beg, T end)
void mooseError(Args &&... args) const
TRI7
AzimuthalBlockSplitGenerator(const InputParameters &parameters)
void addClassDescription(const std::string &doc_string)
Real _start_angle
Starting angular position of the modified azimuthal blocks.
QUAD9
Real _end_angle
Ending angular position of the modified azimuthal blocks.
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 std::vector< subdomain_id_type > _new_block_ids
IDs of the new azimuthal blocks.
void angleModifier(const Real &side_angular_shift, const Real &side_angular_range, const Real &azi_tol, const Real &terminal_angle, const Real &original_down, std::vector< Real >::iterator &original_down_it, const Real &original_up, std::vector< Real >::iterator &original_up_it, Real &azi_to_keep, Real &azi_to_mod)
Modifies the azimuthal angle to perform move the edge of the control drum during rotation.
uint8_t dof_id_type