LCOV - code coverage report
Current view: top level - src/meshgenerators - PolygonConcentricCircleMeshGeneratorBase.C (source / functions) Hit Total Coverage
Test: idaholab/moose reactor: #31405 (292dce) with base fef103 Lines: 552 571 96.7 %
Date: 2025-09-04 07:56:24 Functions: 3 3 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
      10             : #include "PolygonConcentricCircleMeshGeneratorBase.h"
      11             : #include "libmesh/mesh_smoother_laplace.h"
      12             : #include "MooseUtils.h"
      13             : #include "PolygonalMeshGenerationUtils.h"
      14             : #include "MooseMeshUtils.h"
      15             : 
      16             : #include "libmesh/parsed_function.h"
      17             : #include "libmesh/poly2tri_triangulator.h"
      18             : 
      19             : #include <cmath>
      20             : 
      21             : InputParameters
      22        5166 : PolygonConcentricCircleMeshGeneratorBase::validParams()
      23             : {
      24        5166 :   InputParameters params = ConcentricCircleGeneratorBase::validParams();
      25       10332 :   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       15498 :   params.addRangeCheckedParam<unsigned int>(
      31             :       "background_intervals",
      32       10332 :       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       15498 :   params.addRangeCheckedParam<Real>(
      37             :       "background_radial_bias",
      38       10332 :       1.0,
      39             :       "background_radial_bias>0",
      40             :       "Value used to create biasing in radial meshing for background region.");
      41       15498 :   params.addRangeCheckedParam<Real>(
      42             :       "background_inner_boundary_layer_width",
      43       10332 :       0.0,
      44             :       "background_inner_boundary_layer_width>=0",
      45             :       "Width of background region that is assigned to be the inner boundary layer.");
      46       15498 :   params.addRangeCheckedParam<unsigned int>(
      47             :       "background_inner_boundary_layer_intervals",
      48       10332 :       1,
      49             :       "background_inner_boundary_layer_intervals>0",
      50             :       "Number of radial intervals of the background inner boundary layer");
      51       15498 :   params.addRangeCheckedParam<Real>(
      52             :       "background_inner_boundary_layer_bias",
      53       10332 :       1.0,
      54             :       "background_inner_boundary_layer_bias>0",
      55             :       "Growth factor used for mesh biasing of the background inner boundary layer.");
      56       15498 :   params.addRangeCheckedParam<Real>(
      57             :       "background_outer_boundary_layer_width",
      58       10332 :       0.0,
      59             :       "background_outer_boundary_layer_width>=0",
      60             :       "Width of background region that is assigned to be the outer boundary layer.");
      61       15498 :   params.addRangeCheckedParam<unsigned int>(
      62             :       "background_outer_boundary_layer_intervals",
      63       10332 :       1,
      64             :       "background_outer_boundary_layer_intervals>0",
      65             :       "Number of radial intervals of the background outer boundary layer");
      66       15498 :   params.addRangeCheckedParam<Real>(
      67             :       "background_outer_boundary_layer_bias",
      68       10332 :       1.0,
      69             :       "background_outer_boundary_layer_bias>0",
      70             :       "Growth factor used for mesh biasing of the background outer boundary layer.");
      71       10332 :   params.addParam<std::vector<subdomain_id_type>>(
      72             :       "background_block_ids", "Optional customized block id for the background block.");
      73       10332 :   params.addParam<std::vector<SubdomainName>>(
      74             :       "background_block_names", "Optional customized block names for the background block.");
      75       10332 :   params.addParam<std::vector<Real>>(
      76             :       "duct_sizes", "Distance(s) from polygon center to duct(s) inner boundaries.");
      77       10332 :   MooseEnum duct_sizes_style("apothem radius", "radius");
      78       10332 :   params.addParam<MooseEnum>(
      79             :       "duct_sizes_style",
      80             :       duct_sizes_style,
      81             :       "Style in which polygon center to duct inner boundary distance is "
      82        5166 :       "given (apothem = center to face, radius = center to vertex). Options: " +
      83        5166 :           duct_sizes_style.getRawNames());
      84       10332 :   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       10332 :   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       10332 :   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       10332 :   params.addParam<std::vector<unsigned int>>(
      97             :       "duct_inner_boundary_layer_intervals",
      98             :       "Number of radial intervals of the duct inner boundary layers");
      99       10332 :   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       10332 :   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       10332 :   params.addParam<std::vector<unsigned int>>(
     108             :       "duct_outer_boundary_layer_intervals",
     109             :       "Number of radial intervals of the duct outer boundary layers");
     110       10332 :   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       10332 :   params.addParam<std::vector<subdomain_id_type>>(
     115             :       "duct_block_ids", "Optional customized block ids for each duct geometry block.");
     116       10332 :   params.addParam<std::vector<SubdomainName>>(
     117             :       "duct_block_names", "Optional customized block names for each duct geometry block.");
     118       10332 :   params.addParam<bool>("uniform_mesh_on_sides",
     119       10332 :                         false,
     120             :                         "Whether the side elements are reorganized to have a uniform size.");
     121       10332 :   params.addParam<unsigned int>("smoothing_max_it",
     122       10332 :                                 0,
     123             :                                 "Number of Laplacian smoothing iterations. This number is "
     124             :                                 "disregarded when duct_sizes is present.");
     125       10332 :   params.addParam<bool>(
     126             :       "flat_side_up",
     127       10332 :       false,
     128             :       "Whether to rotate the generated polygon mesh to ensure that one flat side faces up.");
     129             : 
     130       10332 :   params.addParam<bool>(
     131       10332 :       "quad_center_elements", false, "Whether the center elements are quad or triangular.");
     132       10332 :   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       10332 :   params.addParam<bool>("replace_inner_ring_with_delaunay_mesh",
     138       10332 :                         false,
     139             :                         "True to replace the inner ring mesh with a Delaunay unstructured mesh");
     140       10332 :   params.addParam<std::string>(
     141             :       "inner_ring_desired_area",
     142        5166 :       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       10332 :   params.addParamNamesToGroup(
     147             :       "background_block_ids background_block_names duct_block_ids duct_block_names",
     148             :       "Customized Subdomain/Boundary");
     149       10332 :   params.addParamNamesToGroup("num_sectors_per_side background_intervals duct_intervals "
     150             :                               "uniform_mesh_on_sides",
     151             :                               "General Mesh Density");
     152       10332 :   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       10332 :   params.addParamNamesToGroup("quad_center_elements center_quad_factor "
     158             :                               "replace_inner_ring_with_delaunay_mesh inner_ring_desired_area ",
     159             :                               "Inner ring");
     160             : 
     161        5166 :   addRingAndSectorIDParams(params);
     162        5166 :   params.addClassDescription("This PolygonConcentricCircleMeshGeneratorBase object is a base class "
     163             :                              "to be inherited for polygon mesh generators.");
     164             : 
     165        5166 :   return params;
     166        5166 : }
     167             : 
     168        2626 : PolygonConcentricCircleMeshGeneratorBase::PolygonConcentricCircleMeshGeneratorBase(
     169        2626 :     const InputParameters & parameters)
     170             :   : ConcentricCircleGeneratorBase(parameters),
     171        7836 :     _num_sides(isParamValid("num_sides")
     172        7425 :                    ? getParam<unsigned int>("num_sides")
     173        3533 :                    : (isParamValid("hexagon_size") ? (unsigned int)HEXAGON_NUM_SIDES
     174             :                                                    : (unsigned int)SQUARE_NUM_SIDES)),
     175        5224 :     _duct_sizes_style(getParam<MooseEnum>("duct_sizes_style").template getEnum<PolygonSizeStyle>()),
     176        8210 :     _duct_sizes(isParamValid("duct_sizes") ? getParam<std::vector<Real>>("duct_sizes")
     177             :                                            : std::vector<Real>()),
     178        5972 :     _duct_intervals(isParamValid("duct_intervals")
     179        2612 :                         ? getParam<std::vector<unsigned int>>("duct_intervals")
     180             :                         : std::vector<unsigned int>()),
     181        5246 :     _duct_radial_biases(isParamValid("duct_radial_biases")
     182        2612 :                             ? getParam<std::vector<Real>>("duct_radial_biases")
     183        2612 :                             : std::vector<Real>(_duct_intervals.size(), 1.0)),
     184        7960 :     _duct_inner_boundary_layer_params(
     185        2612 :         {isParamValid("duct_inner_boundary_layer_widths")
     186        2612 :              ? getParam<std::vector<Real>>("duct_inner_boundary_layer_widths")
     187        2588 :              : std::vector<Real>(_duct_intervals.size(), 0.0),
     188             :          std::vector<Real>(),
     189        5224 :          isParamValid("duct_inner_boundary_layer_intervals")
     190        2612 :              ? getParam<std::vector<unsigned int>>("duct_inner_boundary_layer_intervals")
     191        2592 :              : std::vector<unsigned int>(_duct_intervals.size(), 0),
     192        5224 :          isParamValid("duct_inner_boundary_layer_biases")
     193        2612 :              ? getParam<std::vector<Real>>("duct_inner_boundary_layer_biases")
     194        2612 :              : std::vector<Real>(_duct_intervals.size(), 0.0)}),
     195        7964 :     _duct_outer_boundary_layer_params(
     196        2612 :         {isParamValid("duct_outer_boundary_layer_widths")
     197        2612 :              ? getParam<std::vector<Real>>("duct_outer_boundary_layer_widths")
     198        2588 :              : std::vector<Real>(_duct_intervals.size(), 0.0),
     199             :          std::vector<Real>(),
     200        5224 :          isParamValid("duct_outer_boundary_layer_intervals")
     201        2612 :              ? getParam<std::vector<unsigned int>>("duct_outer_boundary_layer_intervals")
     202        2590 :              : std::vector<unsigned int>(_duct_intervals.size(), 0),
     203        5224 :          isParamValid("duct_outer_boundary_layer_biases")
     204        2612 :              ? getParam<std::vector<Real>>("duct_outer_boundary_layer_biases")
     205        2612 :              : std::vector<Real>(_duct_intervals.size(), 0.0)}),
     206        5924 :     _duct_block_ids(isParamValid("duct_block_ids")
     207        2612 :                         ? getParam<std::vector<subdomain_id_type>>("duct_block_ids")
     208             :                         : std::vector<subdomain_id_type>()),
     209        5906 :     _duct_block_names(isParamValid("duct_block_names")
     210        2612 :                           ? getParam<std::vector<SubdomainName>>("duct_block_names")
     211             :                           : std::vector<SubdomainName>()),
     212        5224 :     _has_rings(isParamValid("ring_radii")),
     213        5224 :     _has_ducts(isParamValid("duct_sizes")),
     214        2612 :     _polygon_size_style(
     215        2612 :         isParamValid("polygon_size_style")
     216        5224 :             ? getParam<MooseEnum>("polygon_size_style").template getEnum<PolygonSizeStyle>()
     217        3023 :             : (isParamValid("hexagon_size_style")
     218        3023 :                    ? getParam<MooseEnum>("hexagon_size_style").template getEnum<PolygonSizeStyle>()
     219        2810 :                    : getParam<MooseEnum>("square_size_style")
     220             :                          .template getEnum<PolygonSizeStyle>())),
     221        2612 :     _polygon_size(isParamValid("polygon_size")
     222        7425 :                       ? getParam<Real>("polygon_size")
     223        4157 :                       : (isParamValid("hexagon_size") ? getParam<Real>("hexagon_size")
     224        2810 :                                                       : (getParam<Real>("square_size") /
     225             :                                                          2.0))), // square size is twice as apothem
     226        5224 :     _num_sectors_per_side(getParam<std::vector<unsigned int>>("num_sectors_per_side")),
     227        5224 :     _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        5224 :     _background_radial_bias(getParam<Real>("background_radial_bias")),
     231        7836 :     _background_inner_boundary_layer_params(
     232        2612 :         {getParam<Real>("background_inner_boundary_layer_width"),
     233             :          0.0,
     234        5224 :          getParam<Real>("background_inner_boundary_layer_width") > 0.0
     235        5253 :              ? getParam<unsigned int>("background_inner_boundary_layer_intervals")
     236             :              : 0,
     237        5224 :          getParam<Real>("background_inner_boundary_layer_bias")}),
     238        7836 :     _background_outer_boundary_layer_params(
     239        2612 :         {getParam<Real>("background_outer_boundary_layer_width"),
     240             :          0.0,
     241        5224 :          getParam<Real>("background_outer_boundary_layer_width") > 0.0
     242        5253 :              ? getParam<unsigned int>("background_outer_boundary_layer_intervals")
     243             :              : 0,
     244        5224 :          getParam<Real>("background_outer_boundary_layer_bias")}),
     245        9298 :     _background_block_ids(isParamValid("background_block_ids")
     246        2612 :                               ? getParam<std::vector<subdomain_id_type>>("background_block_ids")
     247             :                               : std::vector<subdomain_id_type>()),
     248        8844 :     _background_block_names(isParamValid("background_block_names")
     249        2612 :                                 ? getParam<std::vector<SubdomainName>>("background_block_names")
     250             :                                 : std::vector<SubdomainName>()),
     251        5224 :     _uniform_mesh_on_sides(getParam<bool>("uniform_mesh_on_sides")),
     252        5224 :     _quad_center_elements(getParam<bool>("quad_center_elements")),
     253        5246 :     _center_quad_factor(isParamValid("center_quad_factor") ? getParam<Real>("center_quad_factor")
     254             :                                                            : 0.0),
     255        5224 :     _smoothing_max_it(getParam<unsigned int>("smoothing_max_it")),
     256        6026 :     _sides_to_adapt(isParamValid("sides_to_adapt")
     257        2612 :                         ? getParam<std::vector<unsigned int>>("sides_to_adapt")
     258             :                         : std::vector<unsigned int>()),
     259        2612 :     _node_id_background_meta(declareMeshProperty<dof_id_type>("node_id_background_meta", 0)),
     260        5238 :     _is_control_drum_meta(declareMeshProperty<bool>("is_control_drum_meta", false))
     261             : {
     262        7836 :   declareMeshProperty<bool>("flat_side_up", getParam<bool>("flat_side_up"));
     263        2612 :   declareMeshProperty<unsigned int>("background_intervals_meta", 0);
     264        5224 :   declareMeshProperty<Real>("pattern_pitch_meta", 0.0);
     265        5224 :   declareMeshProperty<std::vector<Real>>("azimuthal_angle_meta", std::vector<Real>());
     266        2612 :   declareMeshProperty<Real>("max_radius_meta", 0.0);
     267             : 
     268        2612 :   const unsigned short tri_order = _tri_elem_type == TRI_ELEM_TYPE::TRI3 ? 1 : 2;
     269        2612 :   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        2612 :   _order = quad_order;
     275        2612 :   if (_quad_center_elements)
     276             :   {
     277         606 :     if (tri_order != quad_order)
     278          18 :       _tri_elem_type = quad_order == 1 ? TRI_ELEM_TYPE::TRI3 : TRI_ELEM_TYPE::TRI6;
     279             :   }
     280         440 :   else if (_ring_radii.empty() && _background_intervals == 1 &&
     281         247 :            _background_inner_boundary_layer_params.intervals == 0 &&
     282        2253 :            _background_outer_boundary_layer_params.intervals == 0 && _duct_sizes.empty())
     283             :   {
     284         238 :     _order = tri_order;
     285         238 :     if (tri_order != quad_order)
     286          18 :       _quad_elem_type = tri_order == 1 ? QUAD_ELEM_TYPE::QUAD4 : QUAD_ELEM_TYPE::QUAD9;
     287             :   }
     288        1768 :   else if (tri_order != quad_order)
     289           2 :     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        2610 :   if (!_sides_to_adapt.empty() && _num_sides != HEXAGON_NUM_SIDES && _num_sides != SQUARE_NUM_SIDES)
     296           0 :     paramError("sides_to_adapt", "If provided, the generated mesh must be a hexagon or a square.");
     297        5220 :   _pitch = 2.0 * (_polygon_size_style == PolygonSizeStyle::apothem
     298        2610 :                       ? _polygon_size
     299           0 :                       : _polygon_size * std::cos(M_PI / Real(_num_sides)));
     300        5220 :   declareMeshProperty<Real>("pitch_meta", _pitch);
     301        2610 :   if (_inward_interface_boundary_names.size() > 0 &&
     302           0 :       _inward_interface_boundary_names.size() != _duct_sizes.size() + _ring_radii.size())
     303           0 :     paramError("inward_interface_boundary_names",
     304             :                "If provided, the length of this parameter must be identical to the total number of "
     305             :                "interfaces.");
     306        2610 :   if (_outward_interface_boundary_names.size() > 0 &&
     307          38 :       _outward_interface_boundary_names.size() != _duct_sizes.size() + _ring_radii.size())
     308           2 :     paramError("outward_interface_boundary_names",
     309             :                "If provided, the length of this parameter must be identical to the total number of "
     310             :                "interfaces.");
     311        2608 :   const unsigned int num_total_background_layers =
     312        2608 :       _background_intervals + _background_inner_boundary_layer_params.intervals +
     313        2608 :       _background_outer_boundary_layer_params.intervals;
     314        2608 :   if ((_has_rings || num_total_background_layers == 1) && _background_block_ids.size() > 1)
     315           2 :     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        2606 :   if ((_has_rings || num_total_background_layers == 1) && _background_block_names.size() > 1)
     320           2 :     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        2604 :   if (!_has_rings && num_total_background_layers > 1 && _quad_center_elements &&
     325             :       _background_block_ids.size() == 1)
     326          18 :     _background_block_ids.insert(_background_block_ids.begin(), _background_block_ids.front());
     327        2604 :   if ((!_has_rings && _background_intervals > 1) &&
     328         262 :       (!_background_block_ids.empty() && _background_block_ids.size() != 2))
     329           2 :     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        2602 :   if (!_has_rings && num_total_background_layers > 1 && _quad_center_elements &&
     334             :       _background_block_names.size() == 1)
     335          18 :     _background_block_names.insert(_background_block_names.begin(),
     336             :                                    _background_block_names.front());
     337        2602 :   if ((!_has_rings && _background_intervals > 1) &&
     338         264 :       (!_background_block_names.empty() && _background_block_names.size() != 2))
     339           2 :     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        2600 :   if (_num_sectors_per_side.size() != _num_sides)
     344           2 :     paramError("num_sectors_per_side",
     345             :                "This parameter must have a length that is consistent with num_sides.");
     346       15899 :   for (auto it = _num_sectors_per_side.begin(); it != _num_sectors_per_side.end(); ++it)
     347       13303 :     if (*it % 2 == 1)
     348           2 :       paramError("num_sectors_per_side", "This parameter must be even.");
     349        2596 :   declareMeshProperty("num_sectors_per_side_meta", _num_sectors_per_side);
     350             : 
     351        7788 :   if (!getParam<bool>("replace_inner_ring_with_delaunay_mesh") &&
     352        7752 :       isParamSetByUser("inner_ring_desired_area"))
     353           0 :     paramError(
     354             :         "inner_ring_desired_area",
     355             :         "This parameter should be set only when 'replace_inner_ring_with_delaunay_mesh=true'");
     356             : 
     357        2596 :   if (_has_rings)
     358             :   {
     359             :     const unsigned int num_innermost_ring_layers =
     360        1953 :         _ring_inner_boundary_layer_params.intervals.front() + _ring_intervals.front() +
     361        1953 :         _ring_outer_boundary_layer_params.intervals.front();
     362             :     // If conditions are met, duplicate the first element of _ring_block_ids at the start.
     363        1953 :     if (!_ring_block_ids.empty() && _quad_center_elements && num_innermost_ring_layers > 1 &&
     364             :         _ring_block_ids.size() == _ring_intervals.size())
     365           9 :       _ring_block_ids.insert(_ring_block_ids.begin(), _ring_block_ids.front());
     366             :     // check if the number of ring block ids is appropiate
     367        1953 :     if (!_ring_block_ids.empty() &&
     368             :         _ring_block_ids.size() !=
     369        1482 :             (_ring_intervals.size() + (unsigned int)(num_innermost_ring_layers != 1)))
     370             :     {
     371             :       // Create an ostringstream for the debug information
     372           4 :       std::ostringstream debug_info;
     373           6 :       debug_info << "quad_center_elements is : " << (_quad_center_elements ? "true" : "false")
     374             :                  << std::endl;
     375           4 :       debug_info << "ring_block_ids size is : " << _ring_block_ids.size() << std::endl;
     376           4 :       debug_info << "ring_intervals size is : " << _ring_intervals.size() << std::endl;
     377           4 :       debug_info << "number of innermost ring layers is : " << num_innermost_ring_layers
     378             :                  << std::endl;
     379             : 
     380             :       // error message
     381           4 :       if (!_quad_center_elements)
     382             :       {
     383           4 :         paramError(
     384             :             "ring_block_ids",
     385             :             "This parameter must have the appropriate size if it is provided: "
     386           2 :             "Since the number of the innermost ring layers (" +
     387           2 :                 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           0 :             debug_info.str());
     392             :       }
     393             :       else
     394             :       {
     395           4 :         paramError(
     396             :             "ring_block_ids",
     397             :             "This parameter must have the appropriate size if it is provided: "
     398           2 :             "Since the number of the innermost ring layers (" +
     399           2 :                 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           0 :             debug_info.str());
     404             :       }
     405           0 :     }
     406             :     // If conditions are met, duplicate the first element of _ring_block_names at the start.
     407        1949 :     if (!_ring_block_names.empty() && _quad_center_elements && num_innermost_ring_layers > 1 &&
     408             :         _ring_block_names.size() == _ring_intervals.size())
     409           9 :       _ring_block_names.insert(_ring_block_names.begin(), _ring_block_names.front());
     410             :     // check if the number of ring block names is appropiate
     411        1949 :     if (!_ring_block_names.empty() &&
     412             :         _ring_block_names.size() !=
     413        1455 :             (_ring_intervals.size() + (unsigned int)(num_innermost_ring_layers != 1)))
     414             :     {
     415             :       // Create an ostringstream for the debug information
     416           4 :       std::ostringstream debug_info;
     417           6 :       debug_info << "quad_center_elements is : " << (_quad_center_elements ? "true" : "false")
     418             :                  << std::endl;
     419           4 :       debug_info << "ring_block_names size is : " << _ring_block_names.size() << std::endl;
     420           4 :       debug_info << "ring_intervals size is : " << _ring_intervals.size() << std::endl;
     421           4 :       debug_info << "number of innermost ring layers is : " << num_innermost_ring_layers
     422             :                  << std::endl;
     423             : 
     424             :       // error message
     425           4 :       if (!_quad_center_elements)
     426             :       {
     427           4 :         paramError(
     428             :             "ring_block_names",
     429             :             "This parameter must have the appropriate size if it is provided: "
     430           2 :             "Since the number of the innermost ring layers (" +
     431           2 :                 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           0 :             debug_info.str());
     436             :       }
     437             :       else
     438             :       {
     439           4 :         paramError(
     440             :             "ring_block_names",
     441             :             "This parameter must have the appropriate size if it is provided: "
     442           2 :             "Since the number of the innermost ring layers (" +
     443           2 :                 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           0 :             debug_info.str());
     448             :       }
     449           0 :     }
     450        4494 :     for (unsigned int i = 0; i < _ring_radii.size(); i++)
     451             :     {
     452        2549 :       const Real layer_width = _ring_radii[i] - (i == 0 ? 0.0 : _ring_radii[i - 1]);
     453        2549 :       _ring_inner_boundary_layer_params.fractions.push_back(
     454        2549 :           _ring_inner_boundary_layer_params.widths[i] / layer_width);
     455        2549 :       _ring_outer_boundary_layer_params.fractions.push_back(
     456        2549 :           _ring_outer_boundary_layer_params.widths[i] / layer_width);
     457             :     }
     458        4488 :     for (unsigned int i = 0; i < _ring_inner_boundary_layer_params.fractions.size(); i++)
     459        2547 :       if (MooseUtils::absoluteFuzzyEqual(_ring_inner_boundary_layer_params.fractions[i], 0.0) &&
     460        2527 :           _ring_inner_boundary_layer_params.intervals[i] > 0)
     461           2 :         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        2545 :                                                     0.0) &&
     465          20 :                _ring_inner_boundary_layer_params.intervals[i] == 0)
     466           2 :         paramError("ring_inner_boundary_layer_intervals",
     467             :                    "Ring inner boundary layer must have non-zero interval if its thickness is "
     468             :                    "not zero.");
     469        4472 :     for (unsigned int i = 0; i < _ring_outer_boundary_layer_params.fractions.size(); i++)
     470             :     {
     471        2537 :       if (MooseUtils::absoluteFuzzyEqual(_ring_outer_boundary_layer_params.fractions[i], 0.0) &&
     472        2497 :           _ring_outer_boundary_layer_params.intervals[i] > 0)
     473           2 :         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        2535 :                                                     0.0) &&
     477          40 :                _ring_outer_boundary_layer_params.intervals[i] == 0)
     478           2 :         paramError("ring_outer_boundary_layer_intervals",
     479             :                    "Ring outer boundary layer must have non-zero interval if its thickness is "
     480             :                    "not zero.");
     481        2533 :       if (_ring_inner_boundary_layer_params.fractions[i] +
     482             :               _ring_outer_boundary_layer_params.fractions[i] >=
     483             :           1.0)
     484           2 :         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        2578 :   if (_duct_sizes.size() != _duct_intervals.size())
     491           2 :     paramError("duct_sizes", "This parameter and duct_intervals must have the same length.");
     492        2576 :   if (_duct_sizes.size() != _duct_radial_biases.size())
     493           2 :     paramError("duct_sizes", "This parameter and duct_radial_biases must have the same length.");
     494        2574 :   if (!_duct_block_ids.empty() && _duct_block_ids.size() != _duct_intervals.size())
     495           2 :     paramError("duct_block_ids",
     496             :                "This parameter must have the same length as duct_intervals if set.");
     497        2572 :   if (!_duct_block_names.empty() && _duct_block_names.size() != _duct_intervals.size())
     498           2 :     paramError("duct_block_names",
     499             :                "This parameter must have the same length as duct_intervals if set.");
     500        2568 :   if (_duct_sizes.size() != _duct_inner_boundary_layer_params.widths.size() ||
     501        2568 :       _duct_sizes.size() != _duct_inner_boundary_layer_params.intervals.size() ||
     502        2568 :       _duct_sizes.size() != _duct_inner_boundary_layer_params.biases.size() ||
     503        2568 :       _duct_sizes.size() != _duct_outer_boundary_layer_params.widths.size() ||
     504        5138 :       _duct_sizes.size() != _duct_outer_boundary_layer_params.intervals.size() ||
     505             :       _duct_sizes.size() != _duct_outer_boundary_layer_params.biases.size())
     506           2 :     paramError("duct_sizes",
     507             :                "The inner and outer duct boundary layer parameters must have the same sizes as "
     508             :                "duct_sizes.");
     509        2568 :   if (_has_ducts)
     510             :   {
     511         364 :     if (_duct_sizes_style == PolygonSizeStyle::apothem)
     512         786 :       for (unsigned int i = 0; i < _duct_sizes.size(); i++)
     513         438 :         _duct_sizes[i] /= std::cos(M_PI / Real(_num_sides));
     514         468 :     for (unsigned int i = 1; i < _duct_sizes.size(); i++)
     515         106 :       if (_duct_sizes[i] <= _duct_sizes[i - 1])
     516           2 :         paramError("duct_sizes", "This parameter must be strictly ascending.");
     517         362 :     if (_duct_sizes.front() <=
     518         362 :         (_has_rings ? _ring_radii.back() / std::cos(M_PI / Real(_num_sides)) : 0.0))
     519           2 :       paramError("duct_sizes",
     520             :                  "This parameter must be positive and ensures no overlapping with rings.");
     521         360 :     if (_duct_sizes.back() >= _pitch / 2.0 / std::cos(M_PI / Real(_num_sides)))
     522           2 :       paramError("duct_sizes",
     523             :                  "This parameter must ensure that ducts are smaller than the polygon size.");
     524         358 :     if (*std::min_element(_duct_intervals.begin(), _duct_intervals.end()) <= 0)
     525           0 :       paramError("duct_intervals", "Elements of this parameter must be positive.");
     526         358 :     if (_duct_sizes_style == PolygonSizeStyle::apothem)
     527         786 :       for (unsigned int i = 0; i < _duct_sizes.size(); i++)
     528             :       {
     529         438 :         _duct_inner_boundary_layer_params.widths[i] /= std::cos(M_PI / Real(_num_sides));
     530         438 :         _duct_outer_boundary_layer_params.widths[i] /= std::cos(M_PI / Real(_num_sides));
     531             :       }
     532         816 :     for (unsigned int i = 0; i < _duct_sizes.size(); i++)
     533             :     {
     534             :       const Real layer_width =
     535         458 :           (i == _duct_sizes.size() - 1 ? _pitch / 2.0 / std::cos(M_PI / Real(_num_sides))
     536         100 :                                        : _duct_sizes[i + 1]) -
     537         458 :           _duct_sizes[i];
     538         458 :       _duct_inner_boundary_layer_params.fractions.push_back(
     539         458 :           _duct_inner_boundary_layer_params.widths[i] / layer_width);
     540         458 :       _duct_outer_boundary_layer_params.fractions.push_back(
     541         458 :           _duct_outer_boundary_layer_params.widths[i] / layer_width);
     542             :     }
     543         810 :     for (unsigned int i = 0; i < _duct_inner_boundary_layer_params.fractions.size(); i++)
     544         456 :       if (MooseUtils::absoluteFuzzyEqual(_duct_inner_boundary_layer_params.fractions[i], 0.0) &&
     545         418 :           _duct_inner_boundary_layer_params.intervals[i] > 0)
     546           2 :         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         454 :                                                     0.0) &&
     550          38 :                _duct_inner_boundary_layer_params.intervals[i] == 0)
     551           2 :         paramError("duct_inner_boundary_layer_intervals",
     552             :                    "Duct inner boundary layer must have non-zero interval if its thickness is "
     553             :                    "not zero.");
     554         794 :     for (unsigned int i = 0; i < _duct_outer_boundary_layer_params.fractions.size(); i++)
     555             :     {
     556         446 :       if (MooseUtils::absoluteFuzzyEqual(_duct_outer_boundary_layer_params.fractions[i], 0.0) &&
     557         406 :           _duct_outer_boundary_layer_params.intervals[i] > 0)
     558           2 :         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         444 :                                                     0.0) &&
     562          40 :                _duct_outer_boundary_layer_params.intervals[i] == 0)
     563           2 :         paramError("duct_outer_boundary_layer_intervals",
     564             :                    "Duct outer boundary layer must have non-zero interval if its thickness is "
     565             :                    "not zero.");
     566         442 :       if (_duct_inner_boundary_layer_params.fractions[i] +
     567             :               _duct_outer_boundary_layer_params.fractions[i] >=
     568             :           1.0)
     569           2 :         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        2552 :   if (MooseUtils::absoluteFuzzyGreaterThan(_background_inner_boundary_layer_params.width +
     575        2552 :                                                _background_outer_boundary_layer_params.width,
     576             :                                            0.0))
     577             :   {
     578             :     const Real min_background_thickness =
     579          29 :         (_has_ducts ? (_duct_sizes.front() * std::cos(M_PI / Real(_num_sides))) : (_pitch / 2.0)) -
     580          29 :         (_has_rings ? _ring_radii.back() : 0.0);
     581          29 :     if (_background_inner_boundary_layer_params.width +
     582          29 :             _background_outer_boundary_layer_params.width *
     583          29 :                 (_duct_sizes_style == PolygonSizeStyle::apothem
     584          29 :                      ? 1.0
     585          11 :                      : std::cos(M_PI / Real(_num_sides))) >=
     586             :         min_background_thickness)
     587           2 :       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          27 :     if (_duct_sizes_style == PolygonSizeStyle::apothem)
     592          18 :       _background_outer_boundary_layer_params.width /= std::cos(M_PI / Real(_num_sides));
     593             :   }
     594        2550 :   if (!_quad_center_elements && _center_quad_factor)
     595           2 :     paramError("center_quad_factor",
     596             :                "this parameter is only applicable if quad_center_elements is set true.");
     597        2548 :   if (_quad_center_elements)
     598         602 :     declareMeshProperty<subdomain_id_type>(
     599             :         "quad_center_block_id",
     600        1419 :         _has_rings ? (_ring_block_ids.empty() ? _block_id_shift + 1 : _ring_block_ids.front())
     601         215 :                    : (_background_block_ids.empty() ? _block_id_shift + 1
     602         197 :                                                     : _background_block_ids.front()));
     603             :   else
     604        3892 :     declareMeshProperty<subdomain_id_type>("quad_center_block_id",
     605             :                                            libMesh::Elem::invalid_subdomain_id);
     606             : 
     607             :   // declare metadata for internal interface boundaries
     608        5096 :   declareMeshProperty<bool>("interface_boundaries", false);
     609        5096 :   declareMeshProperty<std::set<boundary_id_type>>("interface_boundary_ids", {});
     610        2548 : }
     611             : 
     612             : std::unique_ptr<MeshBase>
     613        2376 : PolygonConcentricCircleMeshGeneratorBase::generate()
     614             : {
     615        2376 :   std::vector<std::unique_ptr<ReplicatedMesh>> input(_input_ptrs.size());
     616        2760 :   for (const auto i : index_range(_input_ptrs))
     617             :   {
     618         772 :     input[i] = dynamic_pointer_cast<ReplicatedMesh>(std::move(*_input_ptrs[i]));
     619         386 :     if (!input[i])
     620           0 :       mooseError("A non-replicated mesh input was supplied but replicated meshes are required.");
     621        1146 :     if ((_order == 1 && (*input[i]->elements_begin())->default_order() != FIRST) ||
     622         412 :         (_order == 2 && (*input[i]->elements_begin())->default_order() == FIRST))
     623           2 :       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       14541 :   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       12167 :     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         384 :           _num_sides == 6 ? ((Real)mesh_index * 60.0 - 150.0) : ((Real)mesh_index * 90.0 - 135.0);
     646         384 :       Real upper_azi = _num_sides == 6 ? ((Real)((mesh_index + 1) % 6) * 60.0 - 150.0)
     647         101 :                                        : ((Real)((mesh_index + 1) % 4) * 90.0 - 135.0);
     648         384 :       _azimuthal_angles_array.push_back(azimuthalAnglesCollector(
     649         384 :           *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        3027 :       for (unsigned int i = 1; i < _azimuthal_angles_array.back().size(); i++)
     653             :       {
     654             :         azimuthal_list.push_back(
     655        2643 :             _num_sides == 6
     656        4728 :                 ? ((Real)mesh_index * 60.0 - 150.0 +
     657        2085 :                    std::atan((_azimuthal_angles_array.back()[i - 1] - 1.0) / std::sqrt(3.0)) *
     658        2085 :                        180.0 / M_PI)
     659         558 :                 : ((Real)mesh_index * 90.0 - 135.0 +
     660         558 :                    std::atan((_azimuthal_angles_array.back()[i - 1] - 1.0)) * 180.0 / M_PI));
     661             :       }
     662         384 :       mesh_input_counter++;
     663             :     }
     664             :     else
     665             :     {
     666       11783 :       _azimuthal_angles_array.push_back(std::vector<Real>());
     667       43901 :       for (unsigned int i = 0; i < _num_sectors_per_side[mesh_index] * _order; i++)
     668             :       {
     669             :         azimuthal_list.push_back(
     670       32118 :             std::atan(
     671       32118 :                 std::tan(M_PI / _num_sides) *
     672       32118 :                 (2.0 * (Real)i / (Real)_num_sectors_per_side[mesh_index] / (Real)_order - 1.0)) *
     673       32118 :                 180.0 / M_PI +
     674       32118 :             (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        2374 :   if (_has_rings)
     680             :   {
     681        1779 :     if (_preserve_volumes)
     682             :     {
     683             :       Real corr_factor =
     684        1779 :           PolygonalMeshGenerationUtils::radiusCorrectionFactor(azimuthal_list, true, _order);
     685        4112 :       for (unsigned int i = 0; i < _ring_radii.size(); i++)
     686        2333 :         ring_radii_corr.push_back(_ring_radii[i] * corr_factor);
     687             :     }
     688             :     else
     689           0 :       ring_radii_corr = _ring_radii;
     690        1779 :     if (ring_radii_corr.back() >= _pitch / 2.0)
     691           2 :       paramError("ring_radii",
     692             :                  "Elements of this parameter must be smaller than polygon apothem (after volume "
     693             :                  "preserve correction if applicable).");
     694        3554 :     setMeshProperty("max_radius_meta", ring_radii_corr.back());
     695             :   }
     696             :   // build the first slice of the polygon.
     697             :   auto mesh0 = buildSimpleSlice(ring_radii_corr,
     698        2372 :                                 _ring_intervals,
     699        2372 :                                 _ring_radial_biases,
     700        2372 :                                 _ring_inner_boundary_layer_params,
     701        2372 :                                 _ring_outer_boundary_layer_params,
     702        2372 :                                 _duct_sizes,
     703        2372 :                                 _duct_intervals,
     704        2372 :                                 _duct_radial_biases,
     705        2372 :                                 _duct_inner_boundary_layer_params,
     706        2372 :                                 _duct_outer_boundary_layer_params,
     707             :                                 _pitch,
     708             :                                 _num_sectors_per_side[0],
     709        2372 :                                 _background_intervals,
     710        2372 :                                 _background_radial_bias,
     711        2372 :                                 _background_inner_boundary_layer_params,
     712        2372 :                                 _background_outer_boundary_layer_params,
     713             :                                 _node_id_background_meta,
     714        2372 :                                 _num_sides,
     715             :                                 1,
     716             :                                 _azimuthal_angles_array[0],
     717        2372 :                                 _block_id_shift,
     718        2372 :                                 _quad_center_elements,
     719        2372 :                                 _center_quad_factor,
     720        2372 :                                 _create_inward_interface_boundaries,
     721        2372 :                                 _create_outward_interface_boundaries,
     722        2372 :                                 _interface_boundary_id_shift,
     723        2372 :                                 _generate_side_specific_boundaries,
     724             :                                 _tri_elem_type,
     725        2372 :                                 _quad_elem_type);
     726             :   // This loop builds add-on slices and stitches them to the first slice
     727       12157 :   for (unsigned int mesh_index = 1; mesh_index < _num_sides; mesh_index++)
     728             :   {
     729             :     auto mesh_tmp = buildSimpleSlice(ring_radii_corr,
     730             :                                      _ring_intervals,
     731             :                                      _ring_radial_biases,
     732             :                                      _ring_inner_boundary_layer_params,
     733             :                                      _ring_outer_boundary_layer_params,
     734             :                                      _duct_sizes,
     735             :                                      _duct_intervals,
     736             :                                      _duct_radial_biases,
     737             :                                      _duct_inner_boundary_layer_params,
     738             :                                      _duct_outer_boundary_layer_params,
     739             :                                      _pitch,
     740             :                                      _num_sectors_per_side[mesh_index],
     741        9785 :                                      _background_intervals,
     742        9785 :                                      _background_radial_bias,
     743             :                                      _background_inner_boundary_layer_params,
     744             :                                      _background_outer_boundary_layer_params,
     745             :                                      _node_id_background_meta,
     746        9785 :                                      _num_sides,
     747             :                                      mesh_index + 1,
     748        9785 :                                      _azimuthal_angles_array[mesh_index],
     749        9785 :                                      _block_id_shift,
     750        9785 :                                      _quad_center_elements,
     751        9785 :                                      _center_quad_factor,
     752        9785 :                                      _create_inward_interface_boundaries,
     753        9785 :                                      _create_outward_interface_boundaries,
     754        9785 :                                      _interface_boundary_id_shift,
     755        9785 :                                      _generate_side_specific_boundaries,
     756             :                                      _tri_elem_type,
     757        9785 :                                      _quad_elem_type);
     758             : 
     759        9785 :     ReplicatedMesh other_mesh(*mesh_tmp);
     760        9785 :     MeshTools::Modification::rotate(other_mesh, 360.0 / _num_sides * mesh_index, 0, 0);
     761        9785 :     mesh0->prepare_for_use();
     762        9785 :     other_mesh.prepare_for_use();
     763        9785 :     mesh0->stitch_meshes(other_mesh, SLICE_BEGIN, SLICE_END, TOLERANCE, true, false);
     764        9785 :     other_mesh.clear();
     765        9785 :   }
     766             : 
     767             :   // An extra step to stich the first and last slices together
     768        2372 :   mesh0->stitch_surfaces(SLICE_BEGIN, SLICE_END, TOLERANCE, true, false);
     769             : 
     770        2372 :   if (!_has_rings && !_has_ducts && _background_intervals == 1)
     771         256 :     MooseMesh::changeBoundaryId(*mesh0, 1 + _interface_boundary_id_shift, OUTER_SIDESET_ID, false);
     772             : 
     773        2372 :   if (!_is_general_polygon)
     774             :   {
     775         367 :     setMeshProperty("azimuthal_angle_meta",
     776         734 :                     azimuthalAnglesCollector(*mesh0, -180.0, 180.0, ANGLE_DEGREE));
     777         734 :     setMeshProperty("pattern_pitch_meta", _pitch);
     778             :   }
     779             : 
     780             :   // Move nodes on the external boundary for force uniform spacing.
     781        2372 :   if (_uniform_mesh_on_sides)
     782             :   {
     783             :     const Real angle_tol = 1.0e-5;
     784             :     std::vector<std::pair<Real, unsigned int>> node_azi_list;
     785          36 :     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          36 :         mesh0->get_boundary_info().build_side_list();
     788          36 :     mesh0->get_boundary_info().build_node_list_from_side_list();
     789             :     std::vector<std::tuple<dof_id_type, boundary_id_type>> node_list =
     790          36 :         mesh0->get_boundary_info().build_node_list();
     791             : 
     792        3276 :     for (unsigned int i = 0; i < node_list.size(); ++i)
     793             :     {
     794        3240 :       if (std::get<1>(node_list[i]) == OUTER_SIDESET_ID)
     795             :       {
     796         720 :         node_azi_list.push_back(
     797         720 :             std::make_pair(atan2((*mesh0->node_ptr(std::get<0>(node_list[i])))(1),
     798         720 :                                  (*mesh0->node_ptr(std::get<0>(node_list[i])))(0)) *
     799         720 :                                180.0 / M_PI,
     800             :                            std::get<0>(node_list[i])));
     801         720 :         if (std::abs(node_azi_list.back().first + 180.0) <= angle_tol)
     802           0 :           node_azi_list.back().first = 180.0;
     803             :       }
     804             :     }
     805          36 :     std::sort(node_azi_list.begin(), node_azi_list.end());
     806         216 :     for (unsigned int i = 0; i < _num_sides; i++)
     807             :     {
     808         900 :       for (unsigned int j = 1; j <= _num_sectors_per_side[i]; j++)
     809             :       {
     810         720 :         Real azi_corr_tmp = atan2((Real)j * 2.0 / (Real)_num_sectors_per_side[i] - 1.0,
     811         720 :                                   1.0 / std::tan(M_PI / (Real)_num_sides));
     812         720 :         Real x_tmp = _pitch / 2.0;
     813         720 :         Real y_tmp = x_tmp * std::tan(azi_corr_tmp);
     814         720 :         nodeCoordRotate(
     815         720 :             x_tmp, y_tmp, (Real)i * 360.0 / (Real)_num_sides - (180.0 - 180.0 / (Real)_num_sides));
     816         720 :         Point p_tmp = Point(x_tmp, y_tmp, 0.0);
     817         720 :         mesh0->add_point(
     818             :             p_tmp,
     819         720 :             node_azi_list[std::accumulate(
     820         720 :                               _num_sectors_per_side.begin(), _num_sectors_per_side.begin() + i, 0) +
     821         720 :                           j - 1]
     822         720 :                 .second);
     823             :       }
     824             :     }
     825          36 :     MeshTools::Modification::rotate(*mesh0, (270.0 - 360.0 / (Real)_num_sides), 0.0, 0.0);
     826          36 :   }
     827             : 
     828        2372 :   setMeshProperty("background_intervals_meta", _background_intervals);
     829             : 
     830        2372 :   if (!_has_ducts && _sides_to_adapt.empty())
     831             :   {
     832        1677 :     libMesh::LaplaceMeshSmoother lms(*mesh0);
     833        1677 :     lms.smooth(_smoothing_max_it);
     834             :   }
     835             : 
     836             :   // Set up customized Block Names and/or IDs
     837        2372 :   unsigned int block_it = 0;
     838        2372 :   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        2372 :   if (_has_rings)
     843             :   {
     844        1777 :     ringBlockIdsNamesPreparer(block_it, ring_block_num, block_ids_old, block_ids_new, block_names);
     845             :   }
     846             :   else
     847             :   {
     848         595 :     if (_background_intervals > 1)
     849             :     {
     850         312 :       block_ids_old.push_back(_block_id_shift + 1 + block_it);
     851         376 :       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         624 :                                 ? (SubdomainName)std::to_string(block_ids_new.back())
     855             :                                 : _background_block_names.front());
     856         312 :       block_it++;
     857             :     }
     858             :   }
     859        2372 :   block_ids_old.push_back(_block_id_shift + 1 + block_it);
     860        4744 :   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        4744 :                             ? (SubdomainName)std::to_string(block_ids_new.back())
     864             :                             : _background_block_names.back());
     865        2372 :   block_it++;
     866        2372 :   if (_has_ducts)
     867             :   {
     868         766 :     for (unsigned int i = 0; i < _duct_intervals.size(); i++)
     869             :     {
     870         428 :       block_ids_old.push_back(_block_id_shift + 1 + block_it);
     871         856 :       block_ids_new.push_back(_duct_block_ids.empty() ? block_ids_old.back() : _duct_block_ids[i]);
     872         428 :       block_names.push_back(_duct_block_names.empty()
     873         856 :                                 ? (SubdomainName)std::to_string(block_ids_new.back())
     874             :                                 : _duct_block_names[i]);
     875         428 :       block_it++;
     876             :     }
     877             :   }
     878             : 
     879        2372 :   assignBlockIdsNames(
     880             :       *mesh0, block_ids_old, block_ids_new, block_names, "ConcentricCircleMeshGenerator");
     881             : 
     882        2370 :   if (_external_boundary_id > 0)
     883         977 :     MooseMesh::changeBoundaryId(*mesh0, OUTER_SIDESET_ID, _external_boundary_id, false);
     884        2370 :   if (!_external_boundary_name.empty())
     885             :   {
     886         995 :     mesh0->get_boundary_info().sideset_name(
     887         995 :         _external_boundary_id > 0 ? _external_boundary_id : (boundary_id_type)OUTER_SIDESET_ID) =
     888         995 :         _external_boundary_name;
     889         995 :     mesh0->get_boundary_info().nodeset_name(
     890         995 :         _external_boundary_id > 0 ? _external_boundary_id : (boundary_id_type)OUTER_SIDESET_ID) =
     891             :         _external_boundary_name;
     892             :   }
     893             : 
     894        2370 :   assignInterfaceBoundaryNames(*mesh0);
     895             : 
     896             :   // add sector ids
     897        4740 :   if (isParamValid("sector_id_name"))
     898          90 :     setSectorExtraIDs(
     899          45 :         *mesh0, getParam<std::string>("sector_id_name"), _num_sides, _num_sectors_per_side);
     900             : 
     901             :   // add ring ids
     902        4740 :   if (isParamValid("ring_id_name"))
     903         162 :     setRingExtraIDs(*mesh0,
     904             :                     getParam<std::string>("ring_id_name"),
     905          54 :                     _num_sides,
     906          54 :                     _num_sectors_per_side,
     907             :                     _ring_intervals,
     908         108 :                     getParam<MooseEnum>("ring_id_assign_type") == "ring_wise",
     909          54 :                     _quad_center_elements);
     910             : 
     911             :   // add internal side set info to metadata
     912        2370 :   if (_create_inward_interface_boundaries || _create_outward_interface_boundaries)
     913             :   {
     914        3236 :     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;
     918        1618 :     if (_create_outward_interface_boundaries)
     919             :     {
     920        1609 :       const unsigned int num_boundary_ids = boundary_ids.size();
     921        9843 :       for (const auto i : make_range(num_boundary_ids))
     922             :       {
     923        8234 :         const unsigned int id = i * 2 + 1 + _interface_boundary_id_shift;
     924        8234 :         auto it = boundary_ids.find(id);
     925        8234 :         if (it != boundary_ids.end())
     926             :         {
     927        2036 :           boundary_ids.erase(it);
     928        2036 :           interface_boundary_ids.insert(id);
     929             :         }
     930             :       }
     931             :     }
     932        1618 :     if (_create_inward_interface_boundaries)
     933             :     {
     934           9 :       const unsigned int num_boundary_ids = boundary_ids.size();
     935         153 :       for (const auto i : make_range(num_boundary_ids))
     936             :       {
     937         144 :         const unsigned int id = i * 2 + 2 + _interface_boundary_id_shift;
     938         144 :         auto it = boundary_ids.find(id);
     939         144 :         if (it != boundary_ids.end())
     940             :         {
     941          36 :           boundary_ids.erase(it);
     942          36 :           interface_boundary_ids.insert(id);
     943             :         }
     944             :       }
     945             :     }
     946        3236 :     setMeshProperty("interface_boundary_ids", interface_boundary_ids);
     947             :   }
     948             : 
     949        2370 :   bool flat_side_up = getMeshProperty<bool>("flat_side_up", name());
     950        2370 :   if (flat_side_up)
     951         925 :     MeshTools::Modification::rotate(*mesh0, 180.0 / (Real)_num_sides, 0.0, 0.0);
     952             :   mesh0->set_isnt_prepared();
     953             : 
     954        5924 :   if (_has_rings && getParam<bool>("replace_inner_ring_with_delaunay_mesh"))
     955             :   {
     956          36 :     if (isParamSetByUser("quad_center_elements"))
     957           0 :       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        4356 :     for (auto & elem : mesh0->element_ptr_range())
     964        2160 :       if (elem->vertex_average().norm() < ring_radii_corr[0])
     965         738 :         deleteable_elems.insert(elem);
     966             : 
     967          18 :     const boundary_id_type boundary_id = MooseMeshUtils::getBoundaryIDs(*mesh0, {"_foo"}, true)[0];
     968             :     BoundaryInfo & boundary_info = mesh0->get_boundary_info();
     969         738 :     for (auto & elem : deleteable_elems)
     970             :     {
     971         720 :       unsigned int n_sides = elem->n_sides();
     972        3240 :       for (unsigned int n = 0; n != n_sides; ++n)
     973             :       {
     974        2520 :         Elem * neighbor = elem->neighbor_ptr(n);
     975        2520 :         if (!neighbor)
     976        1080 :           continue;
     977             : 
     978        1440 :         const unsigned int return_side = neighbor->which_neighbor_am_i(elem);
     979             : 
     980        1440 :         if (neighbor->neighbor_ptr(return_side) == elem)
     981             :         {
     982             :           neighbor->set_neighbor(return_side, nullptr);
     983        1440 :           boundary_info.add_side(neighbor, return_side, boundary_id);
     984             :         }
     985             :       }
     986             : 
     987         720 :       mesh0->delete_elem(elem);
     988             :     }
     989             : 
     990             :     // build the 1d mesh from the new boundary
     991          18 :     auto poly_mesh = MooseMeshUtils::buildBoundaryMesh(*mesh0, boundary_id);
     992             :     Real min_side = std::numeric_limits<Real>::max();
     993         756 :     for (auto & elem : poly_mesh->element_ptr_range())
     994             :     {
     995         360 :       Real l = elem->volume();
     996         360 :       if (l < min_side)
     997             :         min_side = l;
     998          18 :     }
     999             : 
    1000             :     // triangulate with the 1d mesh
    1001          18 :     libMesh::Poly2TriTriangulator poly2tri(*poly_mesh);
    1002          18 :     poly2tri.triangulation_type() = libMesh::TriangulatorInterface::PSLG;
    1003          18 :     poly2tri.set_interpolate_boundary_points(0);
    1004             :     poly2tri.set_refine_boundary_allowed(false);
    1005             :     poly2tri.set_verify_hole_boundaries(false);
    1006          54 :     const auto desired_area = getParam<std::string>("inner_ring_desired_area");
    1007          18 :     if (desired_area != "")
    1008             :     {
    1009           9 :       poly2tri.desired_area() = 0;
    1010           9 :       libMesh::ParsedFunction<Real> area_func{desired_area};
    1011           9 :       poly2tri.set_desired_area_function(&area_func);
    1012           9 :     }
    1013             :     else
    1014           9 :       poly2tri.desired_area() = min_side * min_side;
    1015          18 :     poly2tri.minimum_angle() = 0;
    1016          18 :     poly2tri.smooth_after_generating() = true;
    1017             :     // poly2tri.elem_type() is TRI3 by default
    1018          18 :     if (_tri_elem_type == TRI_ELEM_TYPE::TRI6)
    1019           0 :       poly2tri.elem_type() = libMesh::ElemType::TRI6;
    1020          18 :     else if (_tri_elem_type == TRI_ELEM_TYPE::TRI7)
    1021           0 :       poly2tri.elem_type() = libMesh::ElemType::TRI7;
    1022             :     // let us keep the center point
    1023          18 :     poly_mesh->add_point(libMesh::Point());
    1024          18 :     poly2tri.triangulate();
    1025             :     // keep the old subdomain id
    1026        2628 :     for (auto elem : poly_mesh->element_ptr_range())
    1027        1314 :       elem->subdomain_id() = block_ids_new[0];
    1028             : 
    1029          36 :     if (isParamValid("ring_id_name"))
    1030             :     {
    1031          27 :       if (_ring_intervals[0] != 1 && getParam<MooseEnum>("ring_id_assign_type") == "ring_wise")
    1032           0 :         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          27 :       auto id_name = getParam<std::string>("ring_id_name");
    1039          18 :       const auto extra_id_index = poly_mesh->add_elem_integer(id_name);
    1040        1170 :       for (auto elem : poly_mesh->element_ptr_range())
    1041         585 :         elem->set_extra_integer(extra_id_index, 1);
    1042             :     }
    1043          36 :     if (isParamValid("sector_id_name"))
    1044             :     {
    1045             :       // we will assign all elements in the inner ring with zero sector id
    1046          27 :       auto id_name = getParam<std::string>("sector_id_name");
    1047          18 :       const auto extra_id_index = poly_mesh->add_elem_integer(id_name);
    1048        1170 :       for (auto elem : poly_mesh->element_ptr_range())
    1049         585 :         elem->set_extra_integer(extra_id_index, 0);
    1050             :     }
    1051             : 
    1052             :     // stitch the triangulated mesh and the original mesh without the inner ring
    1053          18 :     mesh0->stitch_meshes(*poly_mesh, boundary_id, 0, TOLERANCE, true, false);
    1054          18 :   }
    1055             : 
    1056        4740 :   return dynamic_pointer_cast<MeshBase>(mesh0);
    1057        2370 : }

Generated by: LCOV version 1.14