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  // Check the order of the input mesh's elements
82  // Meanwhile, record the vertex average of each element for future comparison
83  unsigned short order = 0;
84  std::map<dof_id_type, Point> vertex_avgs;
85  for (const auto & elem : mesh.element_ptr_range())
86  {
87  switch (elem->type())
88  {
89  case TRI3:
90  case QUAD4:
91  if (order == 2)
92  paramError("input", "This mesh contains elements of different orders.");
93  order = 1;
94  break;
95  case TRI6:
96  case TRI7:
97  case QUAD8:
98  case QUAD9:
99  if (order == 1)
100  paramError("input", "This mesh contains elements of different orders.");
101  order = 2;
102  break;
103  default:
104  paramError("input", "This mesh contains elements of unsupported type.");
105  }
106  vertex_avgs[elem->id()] = elem->vertex_average();
107  }
108 
110  MooseMeshUtils::getSubdomainIDs(mesh, getParam<std::vector<SubdomainName>>("old_blocks"));
111  if (_old_block_ids.size() != _new_block_ids.size())
112  paramError("new_block_ids", "This parameter must have the same size as old_blocks.");
113 
114  // Check that the block ids/names exist in the mesh
115  std::set<SubdomainID> mesh_blocks;
116  mesh.subdomain_ids(mesh_blocks);
117 
118  for (unsigned int i = 0; i < _old_block_ids.size(); i++)
119  if (_old_block_ids[i] == Moose::INVALID_BLOCK_ID || !mesh_blocks.count(_old_block_ids[i]))
120  paramError("old_blocks",
121  "This parameter contains blocks that do not exist in the input mesh. Error "
122  "triggered by block: " +
123  getParam<std::vector<SubdomainName>>("old_blocks")[i]);
124 
125  if (std::find(_old_block_ids.begin(),
126  _old_block_ids.end(),
127  getMeshProperty<subdomain_id_type>("quad_center_block_id", _input_name)) !=
128  _old_block_ids.end())
129  paramError(
130  "old_blocks",
131  "This parameter contains a block that involves center quad elements, azimuthal splitting "
132  "is currently not supported in this case.");
133 
134  MeshTools::Modification::rotate(mesh, 90.0, 0.0, 0.0);
136  std::vector<Real> azimuthal_angles_vtx;
137  const bool is_first_value_vertex =
138  order == 1 ? true : MooseUtils::absoluteFuzzyEqual(_azimuthal_angle_meta[0], -180.0);
139  if (order == 1)
140  azimuthal_angles_vtx = _azimuthal_angle_meta;
141  else
142  for (const auto & i : make_range(_azimuthal_angle_meta.size()))
143  if (i % 2 != is_first_value_vertex)
144  azimuthal_angles_vtx.push_back(_azimuthal_angle_meta[i]);
145 
146  // So that this class works for both derived classes of PolygonMeshGeneratorBase
147  auto pattern_pitch_meta = std::max(getMeshProperty<Real>("pitch_meta", _input_name),
148  getMeshProperty<Real>("pattern_pitch_meta", _input_name));
149  setMeshProperty("pattern_pitch_meta", pattern_pitch_meta);
150 
151  Real radiusCorrectionFactor_original =
153  _azimuthal_angle_meta, true, order, is_first_value_vertex)
154  : 1.0;
155 
156  const Real azi_tol = 1.0E-6;
157  const Real rad_tol = 1.0e-6;
158  const Real side_angular_range = 360.0 / (Real)_num_sectors_per_side_meta.size();
159  const Real side_angular_shift =
160  _num_sectors_per_side_meta.size() % 2 == 0 ? 0.0 : side_angular_range / 2.0;
161 
162  _start_angle = _start_angle > 180.0 ? _start_angle - 360.0 : _start_angle;
163  _end_angle = _end_angle > 180.0 ? _end_angle - 360.0 : _end_angle;
164 
165  Real original_start_down;
166  std::vector<Real>::iterator original_start_down_it;
167  Real original_start_up;
168  std::vector<Real>::iterator original_start_up_it;
169 
170  // Identify the mesh azimuthal angles of the elements that need to be modified for the starting
171  // edge of the absorber region.
173  original_start_down,
174  original_start_down_it,
175  original_start_up,
176  original_start_up_it,
177  azimuthal_angles_vtx);
178 
179  Real original_end_down;
180  std::vector<Real>::iterator original_end_down_it;
181  Real original_end_up;
182  std::vector<Real>::iterator original_end_up_it;
183 
184  // Identify the mesh azimuthal angles of the elements that need to be modified for the ending
185  // edge of the absorber region.
187  original_end_down,
188  original_end_down_it,
189  original_end_up,
190  original_end_up_it,
191  azimuthal_angles_vtx);
192 
193  Real azi_to_mod_start;
194  Real azi_to_keep_start;
195 
196  // For the two mesh azimuthal angles identified, determine which need to be moved to the starting
197  // edge position.
198  angleModifier(side_angular_shift,
199  side_angular_range,
200  azi_tol,
201  _start_angle,
202  original_start_down,
203  original_start_down_it,
204  original_start_up,
205  original_start_up_it,
206  azi_to_keep_start,
207  azi_to_mod_start);
208 
209  Real azi_to_mod_end;
210  Real azi_to_keep_end;
211 
212  // For the two mesh azimuthal angles identified, determine which need to be moved to the ending
213  // edge position.
214  angleModifier(side_angular_shift,
215  side_angular_range,
216  azi_tol,
217  _end_angle,
218  original_end_down,
219  original_end_down_it,
220  original_end_up,
221  original_end_up_it,
222  azi_to_keep_end,
223  azi_to_mod_end);
224 
225  if (azi_to_mod_end == azi_to_mod_start)
226  paramError("angle_range",
227  "The azimuthal intervals of the input mesh are too coarse for this parameter.");
228 
229  const auto & side_list = mesh.get_boundary_info().build_side_list();
230 
231  // See if the block that contains the external boundary is modified or not
232  bool external_block_change(false);
233  for (unsigned int i = 0; i < side_list.size(); i++)
234  {
235  if (std::get<2>(side_list[i]) == OUTER_SIDESET_ID)
236  {
237  dof_id_type block_id_tmp = mesh.elem_ref(std::get<0>(side_list[i])).subdomain_id();
238  if (std::find(_old_block_ids.begin(), _old_block_ids.end(), block_id_tmp) !=
239  _old_block_ids.end())
240  external_block_change = true;
241  }
242  }
243 
244  mesh.get_boundary_info().build_node_list_from_side_list();
245  std::vector<std::tuple<dof_id_type, boundary_id_type>> node_list =
246  mesh.get_boundary_info().build_node_list();
247 
248  std::vector<std::pair<Real, dof_id_type>> node_id_keep_start;
249  std::vector<std::pair<Real, dof_id_type>> node_id_mod_start;
250  std::vector<std::pair<Real, dof_id_type>> node_id_keep_end;
251  std::vector<std::pair<Real, dof_id_type>> node_id_mod_end;
252 
253  // Determine which nodes are involved in the elements that are intercepted by the starting/ending
254  // angles
255  Real max_quad_elem_rad(0.0);
256  if (mesh.elem_ref(0).n_vertices() == 4)
257  for (dof_id_type i = 0;
258  i < (_num_sectors_per_side_meta[0] / 2 + 1) * (_num_sectors_per_side_meta[0] / 2 + 1);
259  i++)
260  max_quad_elem_rad =
261  max_quad_elem_rad < mesh.node_ref(i).norm() ? mesh.node_ref(i).norm() : max_quad_elem_rad;
262  for (const auto & node_ptr : as_range(mesh.nodes_begin(), mesh.nodes_end()))
263  {
264  const Node & node = *node_ptr;
265  Real node_azi = atan2(node(1), node(0)) * 180.0 / M_PI;
266  Real node_rad = std::sqrt(node(0) * node(0) + node(1) * node(1));
267  if (node_rad > max_quad_elem_rad + rad_tol &&
268  (std::abs(node_azi - azi_to_mod_start) < azi_tol ||
269  std::abs(std::abs(node_azi - azi_to_mod_start) - 360.0) < azi_tol))
270  node_id_mod_start.push_back(std::make_pair(node_rad, node.id()));
271  if (node_rad > max_quad_elem_rad + rad_tol &&
272  (std::abs(node_azi - azi_to_keep_start) < azi_tol ||
273  std::abs(std::abs(node_azi - azi_to_keep_start) - 360.0) < azi_tol))
274  node_id_keep_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_mod_end) < azi_tol ||
277  std::abs(std::abs(node_azi - azi_to_mod_end) - 360.0) < azi_tol))
278  node_id_mod_end.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_keep_end) < azi_tol ||
281  std::abs(std::abs(node_azi - azi_to_keep_end) - 360.0) < azi_tol))
282  node_id_keep_end.push_back(std::make_pair(node_rad, node.id()));
283  }
284  // Sort the involved nodes using radius as the key; this facilitates the determination of circular
285  // regions nodes
286  std::sort(node_id_mod_start.begin(), node_id_mod_start.end());
287  std::sort(node_id_keep_start.begin(), node_id_keep_start.end());
288  std::sort(node_id_mod_end.begin(), node_id_mod_end.end());
289  std::sort(node_id_keep_end.begin(), node_id_keep_end.end());
290  std::vector<Real> circular_rad_list;
291  std::vector<Real> non_circular_rad_list;
292 
293  // Modify the nodes with the azimuthal angles identified before.
295  node_id_mod_start,
296  node_id_keep_start,
297  circular_rad_list,
298  non_circular_rad_list,
299  node_list,
300  _start_angle,
301  external_block_change,
302  rad_tol);
304  node_id_mod_end,
305  node_id_keep_end,
306  circular_rad_list,
307  non_circular_rad_list,
308  node_list,
309  _end_angle,
310  external_block_change,
311  rad_tol);
312 
313  const Real max_circular_radius =
314  *std::max_element(circular_rad_list.begin(), circular_rad_list.end());
315  const Real min_non_circular_radius =
316  *std::min_element(non_circular_rad_list.begin(), non_circular_rad_list.end());
317 
318  // Before re-correcting the radii, correct the mid-point of the elements that are altered
319  if (order == 2)
320  for (const auto & elem : mesh.element_ptr_range())
321  {
322  const Point & original_vertex_avg = vertex_avgs[elem->id()];
323  const Point & new_vertex_avg = elem->vertex_average();
324  if (MooseUtils::absoluteFuzzyGreaterThan((original_vertex_avg - new_vertex_avg).norm(), 0.0))
325  {
326  if (elem->type() == TRI6 || elem->type() == TRI7)
327  {
328  *elem->node_ptr(3) = midPointCorrector(
329  *elem->node_ptr(0), *elem->node_ptr(1), max_circular_radius, rad_tol);
330  *elem->node_ptr(4) = midPointCorrector(
331  *elem->node_ptr(1), *elem->node_ptr(2), max_circular_radius, rad_tol);
332  *elem->node_ptr(5) = midPointCorrector(
333  *elem->node_ptr(2), *elem->node_ptr(0), max_circular_radius, rad_tol);
334  if (elem->type() == TRI7)
335  *elem->node_ptr(6) = (*elem->node_ptr(0) + *elem->node_ptr(1) + *elem->node_ptr(2) +
336  *elem->node_ptr(3) + *elem->node_ptr(4) + *elem->node_ptr(5)) /
337  6.0;
338  }
339  else if (elem->type() == QUAD8 || elem->type() == QUAD9)
340  {
341  *elem->node_ptr(4) = midPointCorrector(
342  *elem->node_ptr(0), *elem->node_ptr(1), max_circular_radius, rad_tol);
343  *elem->node_ptr(5) = midPointCorrector(
344  *elem->node_ptr(1), *elem->node_ptr(2), max_circular_radius, rad_tol);
345  *elem->node_ptr(6) = midPointCorrector(
346  *elem->node_ptr(2), *elem->node_ptr(3), max_circular_radius, rad_tol);
347  *elem->node_ptr(7) = midPointCorrector(
348  *elem->node_ptr(3), *elem->node_ptr(0), max_circular_radius, rad_tol);
349  if (elem->type() == QUAD9)
350  *elem->node_ptr(8) = (*elem->node_ptr(0) + *elem->node_ptr(1) + *elem->node_ptr(2) +
351  *elem->node_ptr(3) + *elem->node_ptr(4) + *elem->node_ptr(5) +
352  *elem->node_ptr(6) + *elem->node_ptr(7)) /
353  8.0;
354  }
355  }
356  }
357 
358  std::vector<Real> new_azimuthal_angle;
359  for (const auto & node_ptr : as_range(mesh.nodes_begin(), mesh.nodes_end()))
360  {
361  const Node & node = *node_ptr;
363  std::sqrt(node(0) * node(0) + node(1) * node(1)), max_circular_radius, rad_tol))
364  {
365  Real node_azi = atan2(node(1), node(0)) * 180.0 / M_PI;
366  new_azimuthal_angle.push_back(node_azi);
367  }
368  }
369  std::sort(new_azimuthal_angle.begin(), new_azimuthal_angle.end());
370  _azimuthal_angle_meta = new_azimuthal_angle;
371  const bool is_first_value_vertex_mod =
372  order == 1 ? true : MooseUtils::absoluteFuzzyEqual(_azimuthal_angle_meta[0], -180.0);
373 
374  Real radiusCorrectionFactor_mod =
376  _azimuthal_angle_meta, true, order, is_first_value_vertex_mod)
377  : 1.0;
378 
379  // Re-correct Radii
380  if (_preserve_volumes)
381  {
382  if (min_non_circular_radius <
383  max_circular_radius / radiusCorrectionFactor_original * radiusCorrectionFactor_mod)
384  mooseError("In AzimuthalBlockSplitGenerator ",
385  _name,
386  ": the circular region is overlapped with background region after correction.");
387  for (Node * node_ptr : as_range(mesh.nodes_begin(), mesh.nodes_end()))
388  {
389  Node & node = *node_ptr;
390  Real node_rad = std::sqrt(node(0) * node(0) + node(1) * node(1));
391  // Any nodes with radii smaller than the threshold belong to circular regions
392  if (node_rad > rad_tol && node_rad <= max_circular_radius + rad_tol)
393  {
394  const Real node_azi = atan2(node(1), node(0));
395  const Real node_rad_corr =
396  node_rad / radiusCorrectionFactor_original * radiusCorrectionFactor_mod;
397  node(0) = node_rad_corr * std::cos(node_azi);
398  node(1) = node_rad_corr * std::sin(node_azi);
399  }
400  }
401  }
402  // Assign New Block IDs
403  for (unsigned int block_id_index = 0; block_id_index < _old_block_ids.size(); block_id_index++)
404  for (auto & elem :
405  as_range(mesh.active_subdomain_elements_begin(_old_block_ids[block_id_index]),
406  mesh.active_subdomain_elements_end(_old_block_ids[block_id_index])))
407  {
408  auto p_cent = elem->true_centroid();
409  auto p_cent_azi = atan2(p_cent(1), p_cent(0)) * 180.0 / M_PI;
410  if (_start_angle < _end_angle && p_cent_azi >= _start_angle && p_cent_azi <= _end_angle)
411  elem->subdomain_id() = _new_block_ids[block_id_index];
412  else if (_start_angle > _end_angle &&
413  (p_cent_azi >= _start_angle || p_cent_azi <= _end_angle))
414  elem->subdomain_id() = _new_block_ids[block_id_index];
415  }
416  // Assign new Block Names if applicable
417  for (unsigned int i = 0; i < _new_block_names.size(); i++)
418  mesh.subdomain_name(_new_block_ids[i]) = _new_block_names[i];
419  MeshTools::Modification::rotate(mesh, -90.0, 0.0, 0.0);
420  for (unsigned int i = 0; i < _azimuthal_angle_meta.size(); i++)
421  _azimuthal_angle_meta[i] = (_azimuthal_angle_meta[i] - 90.0 <= -180.0)
422  ? (_azimuthal_angle_meta[i] + 270.0)
423  : _azimuthal_angle_meta[i] - 90.0;
424  std::sort(_azimuthal_angle_meta.begin(), _azimuthal_angle_meta.end());
425 
426  return std::move(_input);
427 }
428 
429 void
431  Real & original_down,
432  std::vector<Real>::iterator & original_down_it,
433  Real & original_up,
434  std::vector<Real>::iterator & original_up_it,
435  std::vector<Real> & azimuthal_angles_vtx)
436 {
437 
438  auto term_up =
439  std::lower_bound(azimuthal_angles_vtx.begin(), azimuthal_angles_vtx.end(), terminal_angle);
440  if (term_up == azimuthal_angles_vtx.begin())
441  {
442  original_up = *term_up;
443  original_up_it = term_up;
444  original_down = -180.0;
445  original_down_it = azimuthal_angles_vtx.end() - 1;
446  }
447  else if (term_up == azimuthal_angles_vtx.end())
448  {
449  original_down = azimuthal_angles_vtx.back();
450  original_down_it = azimuthal_angles_vtx.end() - 1;
451  original_up = 180.0;
452  original_up_it = azimuthal_angles_vtx.begin();
453  }
454  else
455  {
456  original_down = *(term_up - 1);
457  original_down_it = term_up - 1;
458  original_up = *term_up;
459  original_up_it = term_up;
460  }
461 }
462 
463 void
464 AzimuthalBlockSplitGenerator::angleModifier(const Real & side_angular_shift,
465  const Real & side_angular_range,
466  const Real & azi_tol,
467  const Real & terminal_angle,
468  const Real & original_down,
469  std::vector<Real>::iterator & original_down_it,
470  const Real & original_up,
471  std::vector<Real>::iterator & original_up_it,
472  Real & azi_to_keep,
473  Real & azi_to_mod)
474 {
475  // Half interval is used to help determine which of the two identified azimuthal angles needs to
476  // be moved.
477  const Real half_interval = (original_up - original_down) / 2.0;
478  // In this case, the lower azimuthal angle matches a vertex position, while the target angle is
479  // not overlapped with the same vertex position. Thus the lower azimuthal angle is not moved
480  // anyway.
481  if (std::abs((original_down + side_angular_shift) / side_angular_range -
482  std::round((original_down + side_angular_shift) / side_angular_range)) < azi_tol &&
483  std::abs(original_down - terminal_angle) > azi_tol)
484  {
485  azi_to_keep = original_down;
486  azi_to_mod = original_up;
487  *original_up_it = terminal_angle;
488  }
489  // In this case, the upper azimuthal angle matches a vertex position, while the target angle is
490  // not overlapped with the same vertex position. Thus the upper azimuthal angle is not moved
491  // anyway.
492  else if (std::abs((original_up + side_angular_shift) / side_angular_range -
493  std::round((original_up + side_angular_shift) / side_angular_range)) <
494  azi_tol &&
495  std::abs(original_up - terminal_angle) > azi_tol)
496  {
497  azi_to_keep = original_up;
498  azi_to_mod = original_down;
499  *original_down_it = terminal_angle;
500  }
501  // Move upper azimuthal angle as it is closer to the target angle.
502  else if (terminal_angle - original_down > half_interval)
503  {
504  azi_to_keep = original_down;
505  azi_to_mod = original_up;
506  *original_up_it = terminal_angle;
507  }
508  // Move lower azimuthal angle as it is closer to the target angle.
509  else
510  {
511  azi_to_keep = original_up;
512  azi_to_mod = original_down;
513  *original_down_it = terminal_angle;
514  }
515 }
516 
517 void
519  ReplicatedMesh & mesh,
520  const std::vector<std::pair<Real, dof_id_type>> & node_id_mod,
521  const std::vector<std::pair<Real, dof_id_type>> & node_id_keep,
522  std::vector<Real> & circular_rad_list,
523  std::vector<Real> & non_circular_rad_list,
524  const std::vector<std::tuple<dof_id_type, boundary_id_type>> & node_list,
525  const Real & term_angle,
526  const bool & external_block_change,
527  const Real & rad_tol)
528 {
529  for (unsigned int i = 0; i < node_id_mod.size(); i++)
530  {
531  // Circular regions, radius is not altered
532  if (std::abs(node_id_mod[i].first - node_id_keep[i].first) < rad_tol)
533  {
534  Node & node_mod = mesh.node_ref(node_id_mod[i].second);
535  circular_rad_list.push_back(node_id_mod[i].first);
536  node_mod(0) = node_id_mod[i].first * std::cos(term_angle * M_PI / 180.0);
537  node_mod(1) = node_id_mod[i].first * std::sin(term_angle * M_PI / 180.0);
538  }
539  else
540  {
541  non_circular_rad_list.push_back(node_id_mod[i].first);
542  // For external boundary nodes, if the external block is not modified, the boundary nodes are
543  // not moved.
544  // Non-circular range, use intercept instead
545  if (std::find(node_list.begin(),
546  node_list.end(),
547  std::make_tuple(node_id_mod[i].second, OUTER_SIDESET_ID)) == node_list.end() ||
548  external_block_change)
549  {
550  Node & node_mod = mesh.node_ref(node_id_mod[i].second);
551  const Node & node_keep = mesh.node_ref(node_id_keep[i].second);
552  std::pair<Real, Real> pair_tmp = fourPointIntercept(
553  std::make_pair(node_mod(0), node_mod(1)),
554  std::make_pair(node_keep(0), node_keep(1)),
555  std::make_pair(0.0, 0.0),
556  std::make_pair(2.0 * node_id_mod[i].first * std::cos(term_angle * M_PI / 180.0),
557  2.0 * node_id_mod[i].first * std::sin(term_angle * M_PI / 180.0)));
558  node_mod(0) = pair_tmp.first;
559  node_mod(1) = pair_tmp.second;
560  }
561  }
562  }
563 }
564 
565 Point
567  const Point vertex_1,
568  const Real max_radius,
569  const Real rad_tol)
570 {
571  // Check if two vertices have the same radius
572  const Real r_vertex_0 = std::sqrt(vertex_0(0) * vertex_0(0) + vertex_0(1) * vertex_0(1));
573  const Real r_vertex_1 = std::sqrt(vertex_1(0) * vertex_1(0) + vertex_1(1) * vertex_1(1));
574  // If both vertices have the same radius and they are located in the circular region,
575  // their midpoint should have the same radius and has the average azimuthal angle of the two
576  // vertices.
577  if (std::abs(r_vertex_0 - r_vertex_1) < rad_tol && r_vertex_0 < max_radius + rad_tol)
578  return (vertex_0 + vertex_1).unit() * r_vertex_0;
579  else
580  return (vertex_0 + vertex_1) / 2.0;
581 }
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)
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
QUAD8
const Real _angle_range
Angular range of the modified azimuthal blocks.
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 T & getParam(const std::string &name) const
void paramError(const std::string &param, Args... args) const
auto norm(const T &a) -> decltype(std::abs(a))
const std::string _name
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.
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)
TRI7
void mooseError(Args &&... args) const
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.
bool absoluteFuzzyGreaterThan(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
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