https://mooseframework.inl.gov
PolygonConcentricCircleMeshGeneratorBase.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 "libmesh/mesh_smoother_laplace.h"
12 #include "MooseUtils.h"
14 #include "MooseMeshUtils.h"
15 
16 #include "libmesh/parsed_function.h"
17 #include "libmesh/poly2tri_triangulator.h"
18 
19 #include <cmath>
20 
23 {
25  params.addRequiredRangeCheckedParam<std::vector<unsigned int>>(
26  "num_sectors_per_side",
27  "num_sectors_per_side>0",
28  "Number of azimuthal sectors per polygon side (rotating counterclockwise from top right "
29  "face).");
30  params.addRangeCheckedParam<unsigned int>(
31  "background_intervals",
32  1,
33  "background_intervals>0",
34  "Number of radial meshing intervals in background region (area "
35  "between rings and ducts) excluding the background's boundary layers.");
36  params.addRangeCheckedParam<Real>(
37  "background_radial_bias",
38  1.0,
39  "background_radial_bias>0",
40  "Value used to create biasing in radial meshing for background region.");
41  params.addRangeCheckedParam<Real>(
42  "background_inner_boundary_layer_width",
43  0.0,
44  "background_inner_boundary_layer_width>=0",
45  "Width of background region that is assigned to be the inner boundary layer.");
46  params.addRangeCheckedParam<unsigned int>(
47  "background_inner_boundary_layer_intervals",
48  1,
49  "background_inner_boundary_layer_intervals>0",
50  "Number of radial intervals of the background inner boundary layer");
51  params.addRangeCheckedParam<Real>(
52  "background_inner_boundary_layer_bias",
53  1.0,
54  "background_inner_boundary_layer_bias>0",
55  "Growth factor used for mesh biasing of the background inner boundary layer.");
56  params.addRangeCheckedParam<Real>(
57  "background_outer_boundary_layer_width",
58  0.0,
59  "background_outer_boundary_layer_width>=0",
60  "Width of background region that is assigned to be the outer boundary layer.");
61  params.addRangeCheckedParam<unsigned int>(
62  "background_outer_boundary_layer_intervals",
63  1,
64  "background_outer_boundary_layer_intervals>0",
65  "Number of radial intervals of the background outer boundary layer");
66  params.addRangeCheckedParam<Real>(
67  "background_outer_boundary_layer_bias",
68  1.0,
69  "background_outer_boundary_layer_bias>0",
70  "Growth factor used for mesh biasing of the background outer boundary layer.");
71  params.addParam<std::vector<subdomain_id_type>>(
72  "background_block_ids", "Optional customized block id for the background block.");
73  params.addParam<std::vector<SubdomainName>>(
74  "background_block_names", "Optional customized block names for the background block.");
75  params.addParam<std::vector<Real>>(
76  "duct_sizes", "Distance(s) from polygon center to duct(s) inner boundaries.");
77  MooseEnum duct_sizes_style("apothem radius", "radius");
78  params.addParam<MooseEnum>(
79  "duct_sizes_style",
80  duct_sizes_style,
81  "Style in which polygon center to duct inner boundary distance is "
82  "given (apothem = center to face, radius = center to vertex). Options: " +
83  duct_sizes_style.getRawNames());
84  params.addRangeCheckedParam<std::vector<unsigned int>>(
85  "duct_intervals",
86  "duct_intervals>0",
87  "Number of meshing intervals in each enclosing duct excluding duct boundary layers.");
88  params.addRangeCheckedParam<std::vector<Real>>(
89  "duct_radial_biases",
90  "duct_radial_biases>0",
91  "Values used to create biasing in radial meshing for duct regions.");
92  params.addRangeCheckedParam<std::vector<Real>>(
93  "duct_inner_boundary_layer_widths",
94  "duct_inner_boundary_layer_widths>=0",
95  "Widths of duct regions that are assigned to be the inner boundary layers.");
96  params.addParam<std::vector<unsigned int>>(
97  "duct_inner_boundary_layer_intervals",
98  "Number of radial intervals of the duct inner boundary layers");
99  params.addRangeCheckedParam<std::vector<Real>>(
100  "duct_inner_boundary_layer_biases",
101  "duct_inner_boundary_layer_biases>0",
102  "Growth factors used for mesh biasing of the duct inner boundary layers.");
103  params.addRangeCheckedParam<std::vector<Real>>(
104  "duct_outer_boundary_layer_widths",
105  "duct_outer_boundary_layer_widths>=0",
106  "Widths of duct regions that are assigned to be the outer boundary layers.");
107  params.addParam<std::vector<unsigned int>>(
108  "duct_outer_boundary_layer_intervals",
109  "Number of radial intervals of the duct outer boundary layers");
110  params.addRangeCheckedParam<std::vector<Real>>(
111  "duct_outer_boundary_layer_biases",
112  "duct_outer_boundary_layer_biases>0",
113  "Growth factors used for mesh biasing of the duct outer boundary layers.");
114  params.addParam<std::vector<subdomain_id_type>>(
115  "duct_block_ids", "Optional customized block ids for each duct geometry block.");
116  params.addParam<std::vector<SubdomainName>>(
117  "duct_block_names", "Optional customized block names for each duct geometry block.");
118  params.addParam<bool>("uniform_mesh_on_sides",
119  false,
120  "Whether the side elements are reorganized to have a uniform size.");
121  params.addParam<unsigned int>("smoothing_max_it",
122  0,
123  "Number of Laplacian smoothing iterations. This number is "
124  "disregarded when duct_sizes is present.");
125  params.addParam<bool>(
126  "flat_side_up",
127  false,
128  "Whether to rotate the generated polygon mesh to ensure that one flat side faces up.");
129 
130  params.addParam<bool>(
131  "quad_center_elements", false, "Whether the center elements are quad or triangular.");
132  params.addRangeCheckedParam<Real>(
133  "center_quad_factor",
134  "center_quad_factor>0&center_quad_factor<1",
135  "A fractional radius factor used to determine the radial positions of transition nodes in "
136  "the center region meshed by quad elements.");
137  params.addParam<bool>("replace_inner_ring_with_delaunay_mesh",
138  false,
139  "True to replace the inner ring mesh with a Delaunay unstructured mesh");
140  params.addParam<std::string>(
141  "inner_ring_desired_area",
142  std::string(),
143  "Desired area as a function of x,y; omit to use the default constant area (square of the "
144  "smallest side length on the ring circle)");
145 
146  params.addParamNamesToGroup(
147  "background_block_ids background_block_names duct_block_ids duct_block_names",
148  "Customized Subdomain/Boundary");
149  params.addParamNamesToGroup("num_sectors_per_side background_intervals duct_intervals "
150  "uniform_mesh_on_sides",
151  "General Mesh Density");
152  params.addParamNamesToGroup(
153  "ring_radial_biases ring_inner_boundary_layer_biases ring_inner_boundary_layer_widths "
154  "ring_inner_boundary_layer_intervals ring_outer_boundary_layer_biases "
155  "ring_outer_boundary_layer_widths ring_outer_boundary_layer_intervals",
156  "Mesh Boundary Layers and Biasing Options");
157  params.addParamNamesToGroup("quad_center_elements center_quad_factor "
158  "replace_inner_ring_with_delaunay_mesh inner_ring_desired_area ",
159  "Inner ring");
160 
161  addRingAndSectorIDParams(params);
162  params.addClassDescription("This PolygonConcentricCircleMeshGeneratorBase object is a base class "
163  "to be inherited for polygon mesh generators.");
164 
165  return params;
166 }
167 
169  const InputParameters & parameters)
170  : ConcentricCircleGeneratorBase(parameters),
171  _num_sides(isParamValid("num_sides")
172  ? getParam<unsigned int>("num_sides")
173  : (isParamValid("hexagon_size") ? (unsigned int)HEXAGON_NUM_SIDES
174  : (unsigned int)SQUARE_NUM_SIDES)),
175  _duct_sizes_style(getParam<MooseEnum>("duct_sizes_style").template getEnum<PolygonSizeStyle>()),
176  _duct_sizes(isParamValid("duct_sizes") ? getParam<std::vector<Real>>("duct_sizes")
177  : std::vector<Real>()),
178  _duct_intervals(isParamValid("duct_intervals")
179  ? getParam<std::vector<unsigned int>>("duct_intervals")
180  : std::vector<unsigned int>()),
181  _duct_radial_biases(isParamValid("duct_radial_biases")
182  ? getParam<std::vector<Real>>("duct_radial_biases")
183  : std::vector<Real>(_duct_intervals.size(), 1.0)),
184  _duct_inner_boundary_layer_params(
185  {isParamValid("duct_inner_boundary_layer_widths")
186  ? getParam<std::vector<Real>>("duct_inner_boundary_layer_widths")
187  : std::vector<Real>(_duct_intervals.size(), 0.0),
188  std::vector<Real>(),
189  isParamValid("duct_inner_boundary_layer_intervals")
190  ? getParam<std::vector<unsigned int>>("duct_inner_boundary_layer_intervals")
191  : std::vector<unsigned int>(_duct_intervals.size(), 0),
192  isParamValid("duct_inner_boundary_layer_biases")
193  ? getParam<std::vector<Real>>("duct_inner_boundary_layer_biases")
194  : std::vector<Real>(_duct_intervals.size(), 0.0)}),
195  _duct_outer_boundary_layer_params(
196  {isParamValid("duct_outer_boundary_layer_widths")
197  ? getParam<std::vector<Real>>("duct_outer_boundary_layer_widths")
198  : std::vector<Real>(_duct_intervals.size(), 0.0),
199  std::vector<Real>(),
200  isParamValid("duct_outer_boundary_layer_intervals")
201  ? getParam<std::vector<unsigned int>>("duct_outer_boundary_layer_intervals")
202  : std::vector<unsigned int>(_duct_intervals.size(), 0),
203  isParamValid("duct_outer_boundary_layer_biases")
204  ? getParam<std::vector<Real>>("duct_outer_boundary_layer_biases")
205  : std::vector<Real>(_duct_intervals.size(), 0.0)}),
206  _duct_block_ids(isParamValid("duct_block_ids")
207  ? getParam<std::vector<subdomain_id_type>>("duct_block_ids")
208  : std::vector<subdomain_id_type>()),
209  _duct_block_names(isParamValid("duct_block_names")
210  ? getParam<std::vector<SubdomainName>>("duct_block_names")
211  : std::vector<SubdomainName>()),
212  _has_rings(isParamValid("ring_radii")),
213  _has_ducts(isParamValid("duct_sizes")),
214  _polygon_size_style(
215  isParamValid("polygon_size_style")
216  ? getParam<MooseEnum>("polygon_size_style").template getEnum<PolygonSizeStyle>()
217  : (isParamValid("hexagon_size_style")
218  ? getParam<MooseEnum>("hexagon_size_style").template getEnum<PolygonSizeStyle>()
219  : getParam<MooseEnum>("square_size_style")
220  .template getEnum<PolygonSizeStyle>())),
221  _polygon_size(isParamValid("polygon_size")
222  ? getParam<Real>("polygon_size")
223  : (isParamValid("hexagon_size") ? getParam<Real>("hexagon_size")
224  : (getParam<Real>("square_size") /
225  2.0))), // square size is twice as apothem
226  _num_sectors_per_side(getParam<std::vector<unsigned int>>("num_sectors_per_side")),
227  _background_intervals(getParam<unsigned int>("background_intervals")),
228  // background is usually a single block; however, when there are no rings, background has two
229  // blocks.
230  _background_radial_bias(getParam<Real>("background_radial_bias")),
231  _background_inner_boundary_layer_params(
232  {getParam<Real>("background_inner_boundary_layer_width"),
233  0.0,
234  getParam<Real>("background_inner_boundary_layer_width") > 0.0
235  ? getParam<unsigned int>("background_inner_boundary_layer_intervals")
236  : 0,
237  getParam<Real>("background_inner_boundary_layer_bias")}),
238  _background_outer_boundary_layer_params(
239  {getParam<Real>("background_outer_boundary_layer_width"),
240  0.0,
241  getParam<Real>("background_outer_boundary_layer_width") > 0.0
242  ? getParam<unsigned int>("background_outer_boundary_layer_intervals")
243  : 0,
244  getParam<Real>("background_outer_boundary_layer_bias")}),
245  _background_block_ids(isParamValid("background_block_ids")
246  ? getParam<std::vector<subdomain_id_type>>("background_block_ids")
247  : std::vector<subdomain_id_type>()),
248  _background_block_names(isParamValid("background_block_names")
249  ? getParam<std::vector<SubdomainName>>("background_block_names")
250  : std::vector<SubdomainName>()),
251  _uniform_mesh_on_sides(getParam<bool>("uniform_mesh_on_sides")),
252  _quad_center_elements(getParam<bool>("quad_center_elements")),
253  _center_quad_factor(isParamValid("center_quad_factor") ? getParam<Real>("center_quad_factor")
254  : 0.0),
255  _smoothing_max_it(getParam<unsigned int>("smoothing_max_it")),
256  _sides_to_adapt(isParamValid("sides_to_adapt")
257  ? getParam<std::vector<unsigned int>>("sides_to_adapt")
258  : std::vector<unsigned int>()),
259  _node_id_background_meta(declareMeshProperty<dof_id_type>("node_id_background_meta", 0)),
260  _is_control_drum_meta(declareMeshProperty<bool>("is_control_drum_meta", false))
261 {
262  declareMeshProperty<bool>("flat_side_up", getParam<bool>("flat_side_up"));
263  declareMeshProperty<unsigned int>("background_intervals_meta", 0);
264  declareMeshProperty<Real>("pattern_pitch_meta", 0.0);
265  declareMeshProperty<std::vector<Real>>("azimuthal_angle_meta", std::vector<Real>());
266  declareMeshProperty<Real>("max_radius_meta", 0.0);
267 
268  const unsigned short tri_order = _tri_elem_type == TRI_ELEM_TYPE::TRI3 ? 1 : 2;
269  const unsigned short quad_order = _quad_elem_type == QUAD_ELEM_TYPE::QUAD4 ? 1 : 2;
270  // 1. If the central elements are quad, then no triangular elements are generated;
271  // 2. If the generated mesh has only one radial layer of triangular elements, then no
272  // quad elements are generated; (For PCCMG, that layer must be background)
273  // 3. Otherwise, both types of elements are generated.
274  _order = quad_order;
275  if (_quad_center_elements)
276  {
277  if (tri_order != quad_order)
278  _tri_elem_type = quad_order == 1 ? TRI_ELEM_TYPE::TRI3 : TRI_ELEM_TYPE::TRI6;
279  }
280  else if (_ring_radii.empty() && _background_intervals == 1 &&
281  _background_inner_boundary_layer_params.intervals == 0 &&
282  _background_outer_boundary_layer_params.intervals == 0 && _duct_sizes.empty())
283  {
284  _order = tri_order;
285  if (tri_order != quad_order)
286  _quad_elem_type = tri_order == 1 ? QUAD_ELEM_TYPE::QUAD4 : QUAD_ELEM_TYPE::QUAD9;
287  }
288  else if (tri_order != quad_order)
289  paramError("tri_element_type",
290  "the element types of triangular and quadrilateral elements must be compatible if "
291  "both types of elements are generated.");
292 
293  // This error message is only reserved for future derived classes. Neither of the current derived
294  // classes will trigger this error.
295  if (!_sides_to_adapt.empty() && _num_sides != HEXAGON_NUM_SIDES && _num_sides != SQUARE_NUM_SIDES)
296  paramError("sides_to_adapt", "If provided, the generated mesh must be a hexagon or a square.");
297  _pitch = 2.0 * (_polygon_size_style == PolygonSizeStyle::apothem
298  ? _polygon_size
299  : _polygon_size * std::cos(M_PI / Real(_num_sides)));
300  declareMeshProperty<Real>("pitch_meta", _pitch);
301  if (_inward_interface_boundary_names.size() > 0 &&
302  _inward_interface_boundary_names.size() != _duct_sizes.size() + _ring_radii.size())
303  paramError("inward_interface_boundary_names",
304  "If provided, the length of this parameter must be identical to the total number of "
305  "interfaces.");
306  if (_outward_interface_boundary_names.size() > 0 &&
307  _outward_interface_boundary_names.size() != _duct_sizes.size() + _ring_radii.size())
308  paramError("outward_interface_boundary_names",
309  "If provided, the length of this parameter must be identical to the total number of "
310  "interfaces.");
311  const unsigned int num_total_background_layers =
312  _background_intervals + _background_inner_boundary_layer_params.intervals +
313  _background_outer_boundary_layer_params.intervals;
314  if ((_has_rings || num_total_background_layers == 1) && _background_block_ids.size() > 1)
315  paramError(
316  "background_block_ids",
317  "This parameter must be either unset or have a unity length when ring_radii is "
318  "provided or the number of background intervals (including boundary layers) is unity.");
319  if ((_has_rings || num_total_background_layers == 1) && _background_block_names.size() > 1)
320  paramError(
321  "background_block_names",
322  "This parameter must be either unset or have a unity length when ring_radii is "
323  "provided or the number of background intervals (including boundary layers) is unity.");
324  if (!_has_rings && num_total_background_layers > 1 && _quad_center_elements &&
325  _background_block_ids.size() == 1)
326  _background_block_ids.insert(_background_block_ids.begin(), _background_block_ids.front());
327  if ((!_has_rings && _background_intervals > 1) &&
328  (!_background_block_ids.empty() && _background_block_ids.size() != 2))
329  paramError("background_block_ids",
330  "This parameter must be either unset or have a length of two when ring_radii is not "
331  "provided and background intervals (including boundary layers) is not unity. It can "
332  "optionally to have a unity length if `quad_center_elements` is enabled.");
333  if (!_has_rings && num_total_background_layers > 1 && _quad_center_elements &&
334  _background_block_names.size() == 1)
335  _background_block_names.insert(_background_block_names.begin(),
336  _background_block_names.front());
337  if ((!_has_rings && _background_intervals > 1) &&
338  (!_background_block_names.empty() && _background_block_names.size() != 2))
339  paramError("background_block_names",
340  "This parameter must be either unset or have a length of two when ring_radii is not "
341  "provided and background intervals (including boundary layers) is not unity. It can "
342  "optionally have a unity length if `quad_center_elements` is enabled.");
343  if (_num_sectors_per_side.size() != _num_sides)
344  paramError("num_sectors_per_side",
345  "This parameter must have a length that is consistent with num_sides.");
346  for (auto it = _num_sectors_per_side.begin(); it != _num_sectors_per_side.end(); ++it)
347  if (*it % 2 == 1)
348  paramError("num_sectors_per_side", "This parameter must be even.");
349  declareMeshProperty("num_sectors_per_side_meta", _num_sectors_per_side);
350 
351  if (!getParam<bool>("replace_inner_ring_with_delaunay_mesh") &&
352  isParamSetByUser("inner_ring_desired_area"))
353  paramError(
354  "inner_ring_desired_area",
355  "This parameter should be set only when 'replace_inner_ring_with_delaunay_mesh=true'");
356 
357  if (_has_rings)
358  {
359  const unsigned int num_innermost_ring_layers =
360  _ring_inner_boundary_layer_params.intervals.front() + _ring_intervals.front() +
361  _ring_outer_boundary_layer_params.intervals.front();
362  // If conditions are met, duplicate the first element of _ring_block_ids at the start.
363  if (!_ring_block_ids.empty() && _quad_center_elements && num_innermost_ring_layers > 1 &&
364  _ring_block_ids.size() == _ring_intervals.size())
365  _ring_block_ids.insert(_ring_block_ids.begin(), _ring_block_ids.front());
366  // check if the number of ring block ids is appropiate
367  if (!_ring_block_ids.empty() &&
368  _ring_block_ids.size() !=
369  (_ring_intervals.size() + (unsigned int)(num_innermost_ring_layers != 1)))
370  {
371  // Create an ostringstream for the debug information
372  std::ostringstream debug_info;
373  debug_info << "quad_center_elements is : " << (_quad_center_elements ? "true" : "false")
374  << std::endl;
375  debug_info << "ring_block_ids size is : " << _ring_block_ids.size() << std::endl;
376  debug_info << "ring_intervals size is : " << _ring_intervals.size() << std::endl;
377  debug_info << "number of innermost ring layers is : " << num_innermost_ring_layers
378  << std::endl;
379 
380  // error message
381  if (!_quad_center_elements)
382  {
383  paramError(
384  "ring_block_ids",
385  "This parameter must have the appropriate size if it is provided: "
386  "Since the number of the innermost ring layers (" +
387  std::to_string(num_innermost_ring_layers) +
388  " :first ring interval and inner/outer boundaries of the first ring) is more than "
389  "one, and we have non-quad central elements, the size of 'ring_block_ids' must be "
390  "equal to the size of 'ring_intervals' + 1.\n",
391  debug_info.str());
392  }
393  else
394  {
395  paramError(
396  "ring_block_ids",
397  "This parameter must have the appropriate size if it is provided: "
398  "Since the number of the innermost ring layers (" +
399  std::to_string(num_innermost_ring_layers) +
400  " :first ring interval and inner/outer boundaries of the first ring) is more than "
401  "one, and we have quad central elements, the size of 'ring_block_ids' can be either"
402  "equal to the size of 'ring_intervals' or 'ring_intervals' + 1.\n",
403  debug_info.str());
404  }
405  }
406  // If conditions are met, duplicate the first element of _ring_block_names at the start.
407  if (!_ring_block_names.empty() && _quad_center_elements && num_innermost_ring_layers > 1 &&
408  _ring_block_names.size() == _ring_intervals.size())
409  _ring_block_names.insert(_ring_block_names.begin(), _ring_block_names.front());
410  // check if the number of ring block names is appropiate
411  if (!_ring_block_names.empty() &&
412  _ring_block_names.size() !=
413  (_ring_intervals.size() + (unsigned int)(num_innermost_ring_layers != 1)))
414  {
415  // Create an ostringstream for the debug information
416  std::ostringstream debug_info;
417  debug_info << "quad_center_elements is : " << (_quad_center_elements ? "true" : "false")
418  << std::endl;
419  debug_info << "ring_block_names size is : " << _ring_block_names.size() << std::endl;
420  debug_info << "ring_intervals size is : " << _ring_intervals.size() << std::endl;
421  debug_info << "number of innermost ring layers is : " << num_innermost_ring_layers
422  << std::endl;
423 
424  // error message
425  if (!_quad_center_elements)
426  {
427  paramError(
428  "ring_block_names",
429  "This parameter must have the appropriate size if it is provided: "
430  "Since the number of the innermost ring layers (" +
431  std::to_string(num_innermost_ring_layers) +
432  " :first ring interval and inner/outer boundaries of the first ring) is more than "
433  "one, and we have non-quad central elements, the size of 'ring_block_names' must "
434  "be equal to the size of 'ring_intervals' + 1.\n",
435  debug_info.str());
436  }
437  else
438  {
439  paramError(
440  "ring_block_names",
441  "This parameter must have the appropriate size if it is provided: "
442  "Since the number of the innermost ring layers (" +
443  std::to_string(num_innermost_ring_layers) +
444  " :first ring interval and inner/outer boundaries of the first ring) is more than "
445  "one, and we have quad central elements, the size of 'ring_block_names' can be "
446  "either equal to the size of 'ring_intervals' or 'ring_intervals' + 1.\n",
447  debug_info.str());
448  }
449  }
450  for (unsigned int i = 0; i < _ring_radii.size(); i++)
451  {
452  const Real layer_width = _ring_radii[i] - (i == 0 ? 0.0 : _ring_radii[i - 1]);
453  _ring_inner_boundary_layer_params.fractions.push_back(
454  _ring_inner_boundary_layer_params.widths[i] / layer_width);
455  _ring_outer_boundary_layer_params.fractions.push_back(
456  _ring_outer_boundary_layer_params.widths[i] / layer_width);
457  }
458  for (unsigned int i = 0; i < _ring_inner_boundary_layer_params.fractions.size(); i++)
459  if (MooseUtils::absoluteFuzzyEqual(_ring_inner_boundary_layer_params.fractions[i], 0.0) &&
460  _ring_inner_boundary_layer_params.intervals[i] > 0)
461  paramError("ring_inner_boundary_layer_intervals",
462  "Ring inner boundary layer must have zero interval if its thickness is zero.");
463  else if (MooseUtils::absoluteFuzzyGreaterThan(_ring_inner_boundary_layer_params.fractions[i],
464  0.0) &&
465  _ring_inner_boundary_layer_params.intervals[i] == 0)
466  paramError("ring_inner_boundary_layer_intervals",
467  "Ring inner boundary layer must have non-zero interval if its thickness is "
468  "not zero.");
469  for (unsigned int i = 0; i < _ring_outer_boundary_layer_params.fractions.size(); i++)
470  {
471  if (MooseUtils::absoluteFuzzyEqual(_ring_outer_boundary_layer_params.fractions[i], 0.0) &&
472  _ring_outer_boundary_layer_params.intervals[i] > 0)
473  paramError("ring_outer_boundary_layer_intervals",
474  "Ring outer boundary layer must have zero interval if its thickness is zero.");
475  else if (MooseUtils::absoluteFuzzyGreaterThan(_ring_outer_boundary_layer_params.fractions[i],
476  0.0) &&
477  _ring_outer_boundary_layer_params.intervals[i] == 0)
478  paramError("ring_outer_boundary_layer_intervals",
479  "Ring outer boundary layer must have non-zero interval if its thickness is "
480  "not zero.");
481  if (_ring_inner_boundary_layer_params.fractions[i] +
482  _ring_outer_boundary_layer_params.fractions[i] >=
483  1.0)
484  paramError("ring_inner_boundary_layer_widths",
485  "Summation of ring_inner_boundary_layer_widths and "
486  "ring_outer_boundary_layer_widths cannot exceeds the ring layer width.");
487  }
488  }
489  // Ducts related error messages
490  if (_duct_sizes.size() != _duct_intervals.size())
491  paramError("duct_sizes", "This parameter and duct_intervals must have the same length.");
492  if (_duct_sizes.size() != _duct_radial_biases.size())
493  paramError("duct_sizes", "This parameter and duct_radial_biases must have the same length.");
494  if (!_duct_block_ids.empty() && _duct_block_ids.size() != _duct_intervals.size())
495  paramError("duct_block_ids",
496  "This parameter must have the same length as duct_intervals if set.");
497  if (!_duct_block_names.empty() && _duct_block_names.size() != _duct_intervals.size())
498  paramError("duct_block_names",
499  "This parameter must have the same length as duct_intervals if set.");
500  if (_duct_sizes.size() != _duct_inner_boundary_layer_params.widths.size() ||
501  _duct_sizes.size() != _duct_inner_boundary_layer_params.intervals.size() ||
502  _duct_sizes.size() != _duct_inner_boundary_layer_params.biases.size() ||
503  _duct_sizes.size() != _duct_outer_boundary_layer_params.widths.size() ||
504  _duct_sizes.size() != _duct_outer_boundary_layer_params.intervals.size() ||
505  _duct_sizes.size() != _duct_outer_boundary_layer_params.biases.size())
506  paramError("duct_sizes",
507  "The inner and outer duct boundary layer parameters must have the same sizes as "
508  "duct_sizes.");
509  if (_has_ducts)
510  {
511  if (_duct_sizes_style == PolygonSizeStyle::apothem)
512  for (unsigned int i = 0; i < _duct_sizes.size(); i++)
513  _duct_sizes[i] /= std::cos(M_PI / Real(_num_sides));
514  for (unsigned int i = 1; i < _duct_sizes.size(); i++)
515  if (_duct_sizes[i] <= _duct_sizes[i - 1])
516  paramError("duct_sizes", "This parameter must be strictly ascending.");
517  if (_duct_sizes.front() <=
518  (_has_rings ? _ring_radii.back() / std::cos(M_PI / Real(_num_sides)) : 0.0))
519  paramError("duct_sizes",
520  "This parameter must be positive and ensures no overlapping with rings.");
521  if (_duct_sizes.back() >= _pitch / 2.0 / std::cos(M_PI / Real(_num_sides)))
522  paramError("duct_sizes",
523  "This parameter must ensure that ducts are smaller than the polygon size.");
524  if (*std::min_element(_duct_intervals.begin(), _duct_intervals.end()) <= 0)
525  paramError("duct_intervals", "Elements of this parameter must be positive.");
526  if (_duct_sizes_style == PolygonSizeStyle::apothem)
527  for (unsigned int i = 0; i < _duct_sizes.size(); i++)
528  {
529  _duct_inner_boundary_layer_params.widths[i] /= std::cos(M_PI / Real(_num_sides));
530  _duct_outer_boundary_layer_params.widths[i] /= std::cos(M_PI / Real(_num_sides));
531  }
532  for (unsigned int i = 0; i < _duct_sizes.size(); i++)
533  {
534  const Real layer_width =
535  (i == _duct_sizes.size() - 1 ? _pitch / 2.0 / std::cos(M_PI / Real(_num_sides))
536  : _duct_sizes[i + 1]) -
537  _duct_sizes[i];
538  _duct_inner_boundary_layer_params.fractions.push_back(
539  _duct_inner_boundary_layer_params.widths[i] / layer_width);
540  _duct_outer_boundary_layer_params.fractions.push_back(
541  _duct_outer_boundary_layer_params.widths[i] / layer_width);
542  }
543  for (unsigned int i = 0; i < _duct_inner_boundary_layer_params.fractions.size(); i++)
544  if (MooseUtils::absoluteFuzzyEqual(_duct_inner_boundary_layer_params.fractions[i], 0.0) &&
545  _duct_inner_boundary_layer_params.intervals[i] > 0)
546  paramError("duct_inner_boundary_layer_intervals",
547  "Duct inner boundary layer must have zero interval if its thickness is zero.");
548  else if (MooseUtils::absoluteFuzzyGreaterThan(_duct_inner_boundary_layer_params.fractions[i],
549  0.0) &&
550  _duct_inner_boundary_layer_params.intervals[i] == 0)
551  paramError("duct_inner_boundary_layer_intervals",
552  "Duct inner boundary layer must have non-zero interval if its thickness is "
553  "not zero.");
554  for (unsigned int i = 0; i < _duct_outer_boundary_layer_params.fractions.size(); i++)
555  {
556  if (MooseUtils::absoluteFuzzyEqual(_duct_outer_boundary_layer_params.fractions[i], 0.0) &&
557  _duct_outer_boundary_layer_params.intervals[i] > 0)
558  paramError("duct_outer_boundary_layer_intervals",
559  "Duct outer boundary layer must have zero interval if its thickness is zero.");
560  else if (MooseUtils::absoluteFuzzyGreaterThan(_duct_outer_boundary_layer_params.fractions[i],
561  0.0) &&
562  _duct_outer_boundary_layer_params.intervals[i] == 0)
563  paramError("duct_outer_boundary_layer_intervals",
564  "Duct outer boundary layer must have non-zero interval if its thickness is "
565  "not zero.");
566  if (_duct_inner_boundary_layer_params.fractions[i] +
567  _duct_outer_boundary_layer_params.fractions[i] >=
568  1.0)
569  paramError("duct_inner_boundary_layer_widths",
570  "Summation of duct_inner_boundary_layer_widths and "
571  "duct_outer_boundary_layer_widths cannot exceeds the duct layer width.");
572  }
573  }
574  if (MooseUtils::absoluteFuzzyGreaterThan(_background_inner_boundary_layer_params.width +
575  _background_outer_boundary_layer_params.width,
576  0.0))
577  {
578  const Real min_background_thickness =
579  (_has_ducts ? (_duct_sizes.front() * std::cos(M_PI / Real(_num_sides))) : (_pitch / 2.0)) -
580  (_has_rings ? _ring_radii.back() : 0.0);
581  if (_background_inner_boundary_layer_params.width +
582  _background_outer_boundary_layer_params.width *
583  (_duct_sizes_style == PolygonSizeStyle::apothem
584  ? 1.0
585  : std::cos(M_PI / Real(_num_sides))) >=
586  min_background_thickness)
587  paramError("background_inner_boundary_layer_width",
588  "The summation of background_inner_boundary_layer_width and "
589  "background_outer_boundary_layer_width must be less than the minimum thickness of "
590  "the background region.");
591  if (_duct_sizes_style == PolygonSizeStyle::apothem)
592  _background_outer_boundary_layer_params.width /= std::cos(M_PI / Real(_num_sides));
593  }
594  if (!_quad_center_elements && _center_quad_factor)
595  paramError("center_quad_factor",
596  "this parameter is only applicable if quad_center_elements is set true.");
597  if (_quad_center_elements)
598  declareMeshProperty<subdomain_id_type>(
599  "quad_center_block_id",
600  _has_rings ? (_ring_block_ids.empty() ? _block_id_shift + 1 : _ring_block_ids.front())
601  : (_background_block_ids.empty() ? _block_id_shift + 1
602  : _background_block_ids.front()));
603  else
604  declareMeshProperty<subdomain_id_type>("quad_center_block_id",
606 
607  // declare metadata for internal interface boundaries
608  declareMeshProperty<bool>("interface_boundaries", false);
609  declareMeshProperty<std::set<boundary_id_type>>("interface_boundary_ids", {});
610 }
611 
612 std::unique_ptr<MeshBase>
614 {
615  std::vector<std::unique_ptr<ReplicatedMesh>> input(_input_ptrs.size());
616  for (const auto i : index_range(_input_ptrs))
617  {
618  input[i] = dynamic_pointer_cast<ReplicatedMesh>(std::move(*_input_ptrs[i]));
619  if (!input[i])
620  mooseError("A non-replicated mesh input was supplied but replicated meshes are required.");
621  if ((_order == 1 && (*input[i]->elements_begin())->default_order() != FIRST) ||
622  (_order == 2 && (*input[i]->elements_begin())->default_order() == FIRST))
623  paramError("tri_element_type",
624  "The order of the input mesh to be adapted to does not match the order of the "
625  "mesh to be generated.");
626  }
627 
628  unsigned int mesh_input_counter = 0;
629  // azimuthal array used for radius radius_correction
630  std::vector<Real> azimuthal_list;
631  // loop over all sides of the polygon to collect azimuthal angles of all nodes
632  for (unsigned int mesh_index = 0; mesh_index < _num_sides; mesh_index++)
633  {
634  // When adaptive boundaries exist (only possible for hexagon meshes thru
635  // `HexagonConcentricCircleAdaptiveBoundaryMeshGenerator` or square meshes thru
636  // `CartesianConcentricCircleAdaptiveBoundaryMeshGenerator`), nodes' azimuthal angle cannot be
637  // arithmetically obtained. Instead, `azimuthalAnglesCollector() is used to get this
638  // information from the mesh directly.`
639  if (std::find(_sides_to_adapt.begin(), _sides_to_adapt.end(), mesh_index) !=
640  _sides_to_adapt.end())
641  {
642  // The following lines only work for hexagon and square; and only a hexagon or a square needs
643  // such functionality.
644  Real lower_azi =
645  _num_sides == 6 ? ((Real)mesh_index * 60.0 - 150.0) : ((Real)mesh_index * 90.0 - 135.0);
646  Real upper_azi = _num_sides == 6 ? ((Real)((mesh_index + 1) % 6) * 60.0 - 150.0)
647  : ((Real)((mesh_index + 1) % 4) * 90.0 - 135.0);
649  *input[mesh_input_counter], lower_azi, upper_azi, ANGLE_TANGENT, _num_sides));
650  // loop over the _azimuthal_angles_array just collected to convert tangent to azimuthal
651  // angles.
652  for (unsigned int i = 1; i < _azimuthal_angles_array.back().size(); i++)
653  {
654  azimuthal_list.push_back(
655  _num_sides == 6
656  ? ((Real)mesh_index * 60.0 - 150.0 +
657  std::atan((_azimuthal_angles_array.back()[i - 1] - 1.0) / std::sqrt(3.0)) *
658  180.0 / M_PI)
659  : ((Real)mesh_index * 90.0 - 135.0 +
660  std::atan((_azimuthal_angles_array.back()[i - 1] - 1.0)) * 180.0 / M_PI));
661  }
662  mesh_input_counter++;
663  }
664  else
665  {
666  _azimuthal_angles_array.push_back(std::vector<Real>());
667  for (unsigned int i = 0; i < _num_sectors_per_side[mesh_index] * _order; i++)
668  {
669  azimuthal_list.push_back(
670  std::atan(
671  std::tan(M_PI / _num_sides) *
672  (2.0 * (Real)i / (Real)_num_sectors_per_side[mesh_index] / (Real)_order - 1.0)) *
673  180.0 / M_PI +
674  (Real)mesh_index * (360.0 / (Real)_num_sides) - (180.0 - 180.0 / (Real)_num_sides));
675  }
676  }
677  }
678  std::vector<Real> ring_radii_corr;
679  if (_has_rings)
680  {
681  if (_preserve_volumes)
682  {
683  Real corr_factor =
685  for (unsigned int i = 0; i < _ring_radii.size(); i++)
686  ring_radii_corr.push_back(_ring_radii[i] * corr_factor);
687  }
688  else
689  ring_radii_corr = _ring_radii;
690  if (ring_radii_corr.back() >= _pitch / 2.0)
691  paramError("ring_radii",
692  "Elements of this parameter must be smaller than polygon apothem (after volume "
693  "preserve correction if applicable).");
694  setMeshProperty("max_radius_meta", ring_radii_corr.back());
695  }
696  // build the first slice of the polygon.
697  auto mesh0 = buildSimpleSlice(ring_radii_corr,
702  _duct_sizes,
707  _pitch,
714  _num_sides,
715  1,
726  // This loop builds add-on slices and stitches them to the first slice
727  for (unsigned int mesh_index = 1; mesh_index < _num_sides; mesh_index++)
728  {
729  auto mesh_tmp = buildSimpleSlice(ring_radii_corr,
734  _duct_sizes,
739  _pitch,
740  _num_sectors_per_side[mesh_index],
746  _num_sides,
747  mesh_index + 1,
748  _azimuthal_angles_array[mesh_index],
758 
759  ReplicatedMesh other_mesh(*mesh_tmp);
760  MeshTools::Modification::rotate(other_mesh, 360.0 / _num_sides * mesh_index, 0, 0);
761  mesh0->prepare_for_use();
762  other_mesh.prepare_for_use();
763  mesh0->stitch_meshes(other_mesh, SLICE_BEGIN, SLICE_END, TOLERANCE, true, false);
764  other_mesh.clear();
765  }
766 
767  // An extra step to stich the first and last slices together
768  mesh0->stitch_surfaces(SLICE_BEGIN, SLICE_END, TOLERANCE, true, false);
769 
772 
773  if (!_is_general_polygon)
774  {
775  setMeshProperty("azimuthal_angle_meta",
776  azimuthalAnglesCollector(*mesh0, -180.0, 180.0, ANGLE_DEGREE));
777  setMeshProperty("pattern_pitch_meta", _pitch);
778  }
779 
780  // Move nodes on the external boundary for force uniform spacing.
782  {
783  const Real angle_tol = 1.0e-5;
784  std::vector<std::pair<Real, unsigned int>> node_azi_list;
785  MeshTools::Modification::rotate(*mesh0, -(270.0 - 360.0 / (Real)_num_sides), 0.0, 0.0);
786  std::vector<std::tuple<dof_id_type, unsigned short int, boundary_id_type>> side_list =
787  mesh0->get_boundary_info().build_side_list();
788  mesh0->get_boundary_info().build_node_list_from_side_list();
789  std::vector<std::tuple<dof_id_type, boundary_id_type>> node_list =
790  mesh0->get_boundary_info().build_node_list();
791 
792  for (unsigned int i = 0; i < node_list.size(); ++i)
793  {
794  if (std::get<1>(node_list[i]) == OUTER_SIDESET_ID)
795  {
796  node_azi_list.push_back(
797  std::make_pair(atan2((*mesh0->node_ptr(std::get<0>(node_list[i])))(1),
798  (*mesh0->node_ptr(std::get<0>(node_list[i])))(0)) *
799  180.0 / M_PI,
800  std::get<0>(node_list[i])));
801  if (std::abs(node_azi_list.back().first + 180.0) <= angle_tol)
802  node_azi_list.back().first = 180.0;
803  }
804  }
805  std::sort(node_azi_list.begin(), node_azi_list.end());
806  for (unsigned int i = 0; i < _num_sides; i++)
807  {
808  for (unsigned int j = 1; j <= _num_sectors_per_side[i]; j++)
809  {
810  Real azi_corr_tmp = atan2((Real)j * 2.0 / (Real)_num_sectors_per_side[i] - 1.0,
811  1.0 / std::tan(M_PI / (Real)_num_sides));
812  Real x_tmp = _pitch / 2.0;
813  Real y_tmp = x_tmp * std::tan(azi_corr_tmp);
815  x_tmp, y_tmp, (Real)i * 360.0 / (Real)_num_sides - (180.0 - 180.0 / (Real)_num_sides));
816  Point p_tmp = Point(x_tmp, y_tmp, 0.0);
817  mesh0->add_point(
818  p_tmp,
819  node_azi_list[std::accumulate(
820  _num_sectors_per_side.begin(), _num_sectors_per_side.begin() + i, 0) +
821  j - 1]
822  .second);
823  }
824  }
825  MeshTools::Modification::rotate(*mesh0, (270.0 - 360.0 / (Real)_num_sides), 0.0, 0.0);
826  }
827 
828  setMeshProperty("background_intervals_meta", _background_intervals);
829 
830  if (!_has_ducts && _sides_to_adapt.empty())
831  {
832  libMesh::LaplaceMeshSmoother lms(*mesh0);
834  }
835 
836  // Set up customized Block Names and/or IDs
837  unsigned int block_it = 0;
838  unsigned ring_block_num = 0;
839  std::vector<subdomain_id_type> block_ids_old;
840  std::vector<subdomain_id_type> block_ids_new;
841  std::vector<SubdomainName> block_names;
842  if (_has_rings)
843  {
844  ringBlockIdsNamesPreparer(block_it, ring_block_num, block_ids_old, block_ids_new, block_names);
845  }
846  else
847  {
848  if (_background_intervals > 1)
849  {
850  block_ids_old.push_back(_block_id_shift + 1 + block_it);
851  block_ids_new.push_back(_background_block_ids.empty() ? block_ids_old.back()
852  : _background_block_ids.front());
853  block_names.push_back(_background_block_names.empty()
854  ? (SubdomainName)std::to_string(block_ids_new.back())
855  : _background_block_names.front());
856  block_it++;
857  }
858  }
859  block_ids_old.push_back(_block_id_shift + 1 + block_it);
860  block_ids_new.push_back(_background_block_ids.empty() ? block_ids_old.back()
861  : _background_block_ids.back());
862  block_names.push_back(_background_block_names.empty()
863  ? (SubdomainName)std::to_string(block_ids_new.back())
864  : _background_block_names.back());
865  block_it++;
866  if (_has_ducts)
867  {
868  for (unsigned int i = 0; i < _duct_intervals.size(); i++)
869  {
870  block_ids_old.push_back(_block_id_shift + 1 + block_it);
871  block_ids_new.push_back(_duct_block_ids.empty() ? block_ids_old.back() : _duct_block_ids[i]);
872  block_names.push_back(_duct_block_names.empty()
873  ? (SubdomainName)std::to_string(block_ids_new.back())
874  : _duct_block_names[i]);
875  block_it++;
876  }
877  }
878 
880  *mesh0, block_ids_old, block_ids_new, block_names, "ConcentricCircleMeshGenerator");
881 
882  if (_external_boundary_id > 0)
884  if (!_external_boundary_name.empty())
885  {
886  mesh0->get_boundary_info().sideset_name(
889  mesh0->get_boundary_info().nodeset_name(
892  }
893 
895 
896  // add sector ids
897  if (isParamValid("sector_id_name"))
899  *mesh0, getParam<std::string>("sector_id_name"), _num_sides, _num_sectors_per_side);
900 
901  // add ring ids
902  if (isParamValid("ring_id_name"))
903  setRingExtraIDs(*mesh0,
904  getParam<std::string>("ring_id_name"),
905  _num_sides,
908  getParam<MooseEnum>("ring_id_assign_type") == "ring_wise",
910 
911  // add internal side set info to metadata
913  {
914  setMeshProperty("interface_boundaries", true);
915  // lists boundary ids assigned to interfaces
916  std::set<boundary_id_type> boundary_ids = mesh0->get_boundary_info().get_boundary_ids();
917  std::set<boundary_id_type> interface_boundary_ids;
919  {
920  const unsigned int num_boundary_ids = boundary_ids.size();
921  for (const auto i : make_range(num_boundary_ids))
922  {
923  const unsigned int id = i * 2 + 1 + _interface_boundary_id_shift;
924  auto it = boundary_ids.find(id);
925  if (it != boundary_ids.end())
926  {
927  boundary_ids.erase(it);
928  interface_boundary_ids.insert(id);
929  }
930  }
931  }
933  {
934  const unsigned int num_boundary_ids = boundary_ids.size();
935  for (const auto i : make_range(num_boundary_ids))
936  {
937  const unsigned int id = i * 2 + 2 + _interface_boundary_id_shift;
938  auto it = boundary_ids.find(id);
939  if (it != boundary_ids.end())
940  {
941  boundary_ids.erase(it);
942  interface_boundary_ids.insert(id);
943  }
944  }
945  }
946  setMeshProperty("interface_boundary_ids", interface_boundary_ids);
947  }
948 
949  bool flat_side_up = getMeshProperty<bool>("flat_side_up", name());
950  if (flat_side_up)
951  MeshTools::Modification::rotate(*mesh0, 180.0 / (Real)_num_sides, 0.0, 0.0);
952  mesh0->set_isnt_prepared();
953 
954  if (_has_rings && getParam<bool>("replace_inner_ring_with_delaunay_mesh"))
955  {
956  if (isParamSetByUser("quad_center_elements"))
957  paramError("quad_center_elements",
958  "Should not be set because the center elements of the inner ring will be replaced "
959  "by a Delaunay mesh with 'replace_inner_ring_with_delaunay_mesh=true'");
960 
961  // remove elements of the inner ring and create an inner-ring external boundary side set
962  std::set<Elem *> deleteable_elems;
963  for (auto & elem : mesh0->element_ptr_range())
964  if (elem->vertex_average().norm() < ring_radii_corr[0])
965  deleteable_elems.insert(elem);
966 
967  const boundary_id_type boundary_id = MooseMeshUtils::getBoundaryIDs(*mesh0, {"_foo"}, true)[0];
968  BoundaryInfo & boundary_info = mesh0->get_boundary_info();
969  for (auto & elem : deleteable_elems)
970  {
971  unsigned int n_sides = elem->n_sides();
972  for (unsigned int n = 0; n != n_sides; ++n)
973  {
974  Elem * neighbor = elem->neighbor_ptr(n);
975  if (!neighbor)
976  continue;
977 
978  const unsigned int return_side = neighbor->which_neighbor_am_i(elem);
979 
980  if (neighbor->neighbor_ptr(return_side) == elem)
981  {
982  neighbor->set_neighbor(return_side, nullptr);
983  boundary_info.add_side(neighbor, return_side, boundary_id);
984  }
985  }
986 
987  mesh0->delete_elem(elem);
988  }
989 
990  // build the 1d mesh from the new boundary
991  auto poly_mesh = MooseMeshUtils::buildBoundaryMesh(*mesh0, boundary_id);
992  Real min_side = std::numeric_limits<Real>::max();
993  for (auto & elem : poly_mesh->element_ptr_range())
994  {
995  Real l = elem->volume();
996  if (l < min_side)
997  min_side = l;
998  }
999 
1000  // triangulate with the 1d mesh
1001  libMesh::Poly2TriTriangulator poly2tri(*poly_mesh);
1003  poly2tri.set_interpolate_boundary_points(0);
1004  poly2tri.set_refine_boundary_allowed(false);
1005  poly2tri.set_verify_hole_boundaries(false);
1006  const auto desired_area = getParam<std::string>("inner_ring_desired_area");
1007  if (desired_area != "")
1008  {
1009  poly2tri.desired_area() = 0;
1010  libMesh::ParsedFunction<Real> area_func{desired_area};
1011  poly2tri.set_desired_area_function(&area_func);
1012  }
1013  else
1014  poly2tri.desired_area() = min_side * min_side;
1015  poly2tri.minimum_angle() = 0;
1016  poly2tri.smooth_after_generating() = true;
1017  // poly2tri.elem_type() is TRI3 by default
1019  poly2tri.elem_type() = libMesh::ElemType::TRI6;
1020  else if (_tri_elem_type == TRI_ELEM_TYPE::TRI7)
1021  poly2tri.elem_type() = libMesh::ElemType::TRI7;
1022  // let us keep the center point
1023  poly_mesh->add_point(libMesh::Point());
1024  poly2tri.triangulate();
1025  // keep the old subdomain id
1026  for (auto elem : poly_mesh->element_ptr_range())
1027  elem->subdomain_id() = block_ids_new[0];
1028 
1029  if (isParamValid("ring_id_name"))
1030  {
1031  if (_ring_intervals[0] != 1 && getParam<MooseEnum>("ring_id_assign_type") == "ring_wise")
1032  paramError("replace_inner_ring_with_delaunay_mesh",
1033  "Inner ring has multple intervals with each being assigned with a different "
1034  "ring id, replacing inner ring with Delaunay mesh will remove this ring id "
1035  "assign type. Either change 'ring_id_assign_type' to block_wise or set the "
1036  "first element of 'ring_intervals' to 1 or set "
1037  "'replace_inner_ring_with_delaunay_mesh' to false to avoid this error.");
1038  auto id_name = getParam<std::string>("ring_id_name");
1039  const auto extra_id_index = poly_mesh->add_elem_integer(id_name);
1040  for (auto elem : poly_mesh->element_ptr_range())
1041  elem->set_extra_integer(extra_id_index, 1);
1042  }
1043  if (isParamValid("sector_id_name"))
1044  {
1045  // we will assign all elements in the inner ring with zero sector id
1046  auto id_name = getParam<std::string>("sector_id_name");
1047  const auto extra_id_index = poly_mesh->add_elem_integer(id_name);
1048  for (auto elem : poly_mesh->element_ptr_range())
1049  elem->set_extra_integer(extra_id_index, 0);
1050  }
1051 
1052  // stitch the triangulated mesh and the original mesh without the inner ring
1053  mesh0->stitch_meshes(*poly_mesh, boundary_id, 0, TOLERANCE, true, false);
1054  }
1055 
1056  return dynamic_pointer_cast<MeshBase>(mesh0);
1057 }
std::vector< Real > _duct_sizes
Size parameters of the duct regions.
std::vector< std::unique_ptr< MeshBase > * > _input_ptrs
Pointers to input mesh pointers.
const std::vector< unsigned int > _ring_intervals
Number of rings in each circle or in the enclosing square.
const boundary_id_type _interface_boundary_id_shift
Shift in default boundary IDs of interfaces to avert potential conflicts.
const std::vector< Real > _duct_radial_biases
Bias values used to induce biasing to radial meshing in duct regions.
dof_id_type & _node_id_background_meta
MeshMetaData: maximum node id of the background region.
void addRequiredRangeCheckedParam(const std::string &name, const std::string &parsed_function, const std::string &doc_string)
const Real _center_quad_factor
A fractional radius factor used to determine the radial positions of transition nodes in the center r...
const unsigned int _smoothing_max_it
Maximum smooth iteration number.
std::unique_ptr< ReplicatedMesh > buildBoundaryMesh(const ReplicatedMesh &input_mesh, const boundary_id_type boundary_id)
T & setMeshProperty(const std::string &data_name, Args &&... args)
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
virtual void smooth() override
void setSectorExtraIDs(MeshBase &mesh, const std::string id_name, const unsigned int num_sides, const std::vector< unsigned int > num_sectors_per_side)
assign sector extra ids to polygon mesh
const bool _has_rings
Whether the generated mesh contains ring regions.
virtual void set_refine_boundary_allowed(bool refine_bdy_allowed) override
const subdomain_id_type _block_id_shift
Shift in default subdomain IDs to avert potential conflicts with other meshes.
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
QUAD_ELEM_TYPE _quad_elem_type
Type of quadrilateral elements to be generated.
const bool _preserve_volumes
Volume preserving function is optional.
const std::vector< unsigned int > _num_sectors_per_side
Mesh sector number of each polygon side.
void set_verify_hole_boundaries(bool v)
const boundary_id_type _external_boundary_id
Boundary ID of the mesh&#39;s external boundary.
FIRST
const bool _uniform_mesh_on_sides
Whether the nodes on the external boundary needs to be uniformly distributed.
multiBdryLayerParams _ring_inner_boundary_layer_params
Widths, fractions, radial sectors and growth factors of the inner boundary layers of the ring regions...
const std::vector< Real > _ring_radii
Radii of concentric circles.
void set_interpolate_boundary_points(int n_points)
std::unique_ptr< T_DEST, T_DELETER > dynamic_pointer_cast(std::unique_ptr< T_SRC, T_DELETER > &src)
void assignBlockIdsNames(ReplicatedMesh &mesh, std::vector< subdomain_id_type > &block_ids_old, std::vector< subdomain_id_type > &block_ids_new, std::vector< SubdomainName > &block_names, const std::string &generator_name) const
Assign block IDs and names to the mesh if applicable.
void ringBlockIdsNamesPreparer(unsigned int &block_counter, unsigned int &ring_block_num, std::vector< subdomain_id_type > &block_ids_old, std::vector< subdomain_id_type > &block_ids_new, std::vector< SubdomainName > &block_names) const
Prepare user-defined ring block IDs and names to replace the default ones.
void changeBoundaryId(const boundary_id_type old_id, const boundary_id_type new_id, bool delete_prev)
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.
std::string getRawNames() const
const bool _create_outward_interface_boundaries
Whether outward interface boundaries are created.
virtual const std::string & name() const
std::vector< SubdomainName > _background_block_names
Subdomain Names of the background regions.
singleBdryLayerParams _background_inner_boundary_layer_params
Width, fraction, radiation sectors and growth factor of the inner boundary layer of the background re...
void nodeCoordRotate(Real &x, Real &y, const Real theta) const
Calculates x and y coordinates after rotating by theta angle.
TriangulationType & triangulation_type()
bool isParamValid(const std::string &name) const
static const subdomain_id_type invalid_subdomain_id
multiBdryLayerParams _ring_outer_boundary_layer_params
Widths, fractions, radial sectors and growth factors of the outer boundary layers of the ring regions...
int8_t boundary_id_type
const bool _quad_center_elements
Whether the central elements need to be QUAD4.
void setRingExtraIDs(MeshBase &mesh, const std::string id_name, const unsigned int num_sides, const std::vector< unsigned int > num_sectors_per_side, const std::vector< unsigned int > ring_intervals, const bool ring_wise_id, const bool quad_center_elements)
assign ring extra ids to polygon mesh
virtual void set_desired_area_function(FunctionBase< Real > *desired) override
const bool _create_inward_interface_boundaries
Whether inward interface boundaries are created.
singleBdryLayerParams _background_outer_boundary_layer_params
Width, fraction, radiation sectors and growth factor of the outer boundary layer of the background re...
multiBdryLayerParams _duct_outer_boundary_layer_params
Widths, fractions, radial sectors and growth factors of the inner boundary layers of the duct regions...
const std::vector< Real > _ring_radial_biases
Bias values used to induce biasing to radial meshing in ring regions.
std::vector< BoundaryID > getBoundaryIDs(const libMesh::MeshBase &mesh, const std::vector< BoundaryName > &boundary_name, bool generate_unknown, const std::set< BoundaryID > &mesh_boundary_ids)
void paramError(const std::string &param, Args... args) const
std::vector< subdomain_id_type > _background_block_ids
Subdomain IDs of the background regions.
std::vector< std::vector< Real > > _azimuthal_angles_array
Azimuthal angles of all radial nodes for volume preservation.
static void addRingAndSectorIDParams(InputParameters &params)
Add InputParameters which are used by ring and sector IDs.
const std::vector< SubdomainName > _duct_block_names
Subdomain Names of the duct regions.
const unsigned int _num_sides
Number of polygon sides.
virtual void triangulate() override
bool isParamSetByUser(const std::string &nm) const
const std::vector< unsigned int > _duct_intervals
Number of layers in each enclosing duct.
const BoundaryName _external_boundary_name
Boundary Name of the mesh&#39;s external boundary.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const Real _background_radial_bias
Bias value used to induce biasing to radial meshing in background region.
std::unique_ptr< ReplicatedMesh > buildSimpleSlice(std::vector< Real > ring_radii, const std::vector< unsigned int > ring_layers, const std::vector< Real > ring_radial_biases, const multiBdryLayerParams &ring_inner_boundary_layer_params, const multiBdryLayerParams &ring_outer_boundary_layer_params, std::vector< Real > ducts_center_dist, const std::vector< unsigned int > ducts_layers, const std::vector< Real > duct_radial_biases, const multiBdryLayerParams &duct_inner_boundary_layer_params, const multiBdryLayerParams &duct_outer_boundary_layer_params, const Real pitch, const unsigned int num_sectors_per_side, const unsigned int background_intervals, const Real background_radial_bias, const singleBdryLayerParams &background_inner_boundary_layer_params, const singleBdryLayerParams &background_outer_boundary_layer_params, dof_id_type &node_id_background_meta, const unsigned int side_number, const unsigned int side_index, const std::vector< Real > azimuthal_tangent=std::vector< Real >(), const subdomain_id_type block_id_shift=0, const bool quad_center_elements=false, const Real center_quad_factor=0.0, const bool create_inward_interface_boundaries=false, const bool create_outward_interface_boundaries=true, const boundary_id_type boundary_id_shift=0, const bool generate_side_specific_boundaries=true, const TRI_ELEM_TYPE tri_elem_type=TRI_ELEM_TYPE::TRI3, const QUAD_ELEM_TYPE quad_elem_type=QUAD_ELEM_TYPE::QUAD4)
Creates a mesh of a slice that corresponds to a single side of the polygon to be generated.
const unsigned int _background_intervals
Numbers of radial intervals of the background regions.
bool _is_general_polygon
MeshMetaData: whether this produced mesh is a general polygon (or a hexagon)
IntRange< T > make_range(T beg, T end)
void mooseError(Args &&... args) const
void addClassDescription(const std::string &doc_string)
const bool _has_ducts
Whether the generated mesh contains duct regions.
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)
This ConcentricCircleGeneratorBase object is a base class to be inherited for mesh generators that in...
PolygonConcentricCircleMeshGeneratorBase(const InputParameters &parameters)
TRI_ELEM_TYPE _tri_elem_type
Type of triangular elements to be generated.
const bool _generate_side_specific_boundaries
Whether the side-specific external boundaries are generated or not.
unsigned short _order
Order of the elements to be generated.
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 > _duct_block_ids
Subdomain IDs of the duct regions.
multiBdryLayerParams _duct_inner_boundary_layer_params
Widths, fractions, radial sectors and growth factors of the inner boundary layers of the duct regions...
const std::vector< unsigned int > _sides_to_adapt
Indices of the hexagon sides that need to adapt.
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)
void assignInterfaceBoundaryNames(ReplicatedMesh &mesh) const
Assign interface boundary names to the mesh if applicable.
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
PolygonSizeStyle
An enum class for style of input polygon size.