LCOV - code coverage report
Current view: top level - src/meshgenerators - PeripheralRingMeshGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose reactor: #31405 (292dce) with base fef103 Lines: 270 277 97.5 %
Date: 2025-09-04 07:56:24 Functions: 4 4 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 "PeripheralRingMeshGenerator.h"
      11             : 
      12             : #include "MooseMeshUtils.h"
      13             : #include "MooseUtils.h"
      14             : #include "LinearInterpolation.h"
      15             : #include "FillBetweenPointVectorsTools.h"
      16             : #include "PolygonalMeshGenerationUtils.h"
      17             : #include "libmesh/int_range.h"
      18             : 
      19             : // C++ includes
      20             : #include <cmath> // for atan2
      21             : 
      22             : registerMooseObject("ReactorApp", PeripheralRingMeshGenerator);
      23             : 
      24             : InputParameters
      25         240 : PeripheralRingMeshGenerator::validParams()
      26             : {
      27         240 :   InputParameters params = PolygonMeshGeneratorBase::validParams();
      28         480 :   params.addRequiredParam<MeshGeneratorName>("input", "The input mesh to be modified.");
      29         480 :   params.addParam<bool>(
      30             :       "force_input_centroid_as_center",
      31         480 :       false,
      32             :       "Whether to enforce use of the centroid position of the input mesh as the "
      33             :       "center of the peripheral ring by translating the input mesh to the origin.");
      34             : 
      35         720 :   params.addRangeCheckedParam<unsigned int>(
      36             :       "peripheral_layer_num",
      37         480 :       3,
      38             :       "peripheral_layer_num>0",
      39             :       "The radial layers of the peripheral ring to be added.");
      40         720 :   params.addRangeCheckedParam<Real>(
      41             :       "peripheral_radial_bias",
      42         480 :       1.0,
      43             :       "peripheral_radial_bias>0",
      44             :       "Value used to create biasing in radial meshing for peripheral ring region.");
      45         720 :   params.addRangeCheckedParam<Real>(
      46             :       "peripheral_inner_boundary_layer_width",
      47         480 :       0.0,
      48             :       "peripheral_inner_boundary_layer_width>=0",
      49             :       "Width of peripheral ring region that is assigned to be the inner boundary layer.");
      50         720 :   params.addRangeCheckedParam<unsigned int>(
      51             :       "peripheral_inner_boundary_layer_intervals",
      52         480 :       1,
      53             :       "peripheral_inner_boundary_layer_intervals>0",
      54             :       "Number of radial intervals of the peripheral ring inner boundary layer");
      55         720 :   params.addRangeCheckedParam<Real>(
      56             :       "peripheral_inner_boundary_layer_bias",
      57         480 :       1.0,
      58             :       "peripheral_inner_boundary_layer_bias>0",
      59             :       "Growth factor used for mesh biasing of the peripheral ring inner boundary layer.");
      60         720 :   params.addRangeCheckedParam<Real>(
      61             :       "peripheral_outer_boundary_layer_width",
      62         480 :       0.0,
      63             :       "peripheral_outer_boundary_layer_width>=0",
      64             :       "Width of peripheral ring region that is assigned to be the outer boundary layer.");
      65         720 :   params.addRangeCheckedParam<unsigned int>(
      66             :       "peripheral_outer_boundary_layer_intervals",
      67         480 :       1,
      68             :       "peripheral_outer_boundary_layer_intervals>0",
      69             :       "Number of radial intervals of the peripheral ring outer boundary layer");
      70         720 :   params.addRangeCheckedParam<Real>(
      71             :       "peripheral_outer_boundary_layer_bias",
      72         480 :       1.0,
      73             :       "peripheral_outer_boundary_layer_bias>0",
      74             :       "Growth factor used for mesh biasing of the peripheral ring outer boundary layer.");
      75         480 :   params.addRequiredRangeCheckedParam<Real>("peripheral_ring_radius",
      76             :                                             "peripheral_ring_radius>0",
      77             :                                             "Radius of the peripheral ring to be added.");
      78         480 :   params.addParam<bool>(
      79             :       "preserve_volumes",
      80         480 :       true,
      81             :       "Whether the volume of the peripheral region is preserved by fixing the radius.");
      82         480 :   params.addRequiredParam<BoundaryName>("input_mesh_external_boundary",
      83             :                                         "The external boundary of the input mesh.");
      84         480 :   params.addRequiredParam<subdomain_id_type>(
      85             :       "peripheral_ring_block_id", "The block id assigned to the created peripheral layer.");
      86         480 :   params.addParam<SubdomainName>("peripheral_ring_block_name",
      87             :                                  "The block name assigned to the created peripheral layer.");
      88         480 :   params.addRangeCheckedParam<boundary_id_type>("external_boundary_id",
      89             :                                                 "external_boundary_id>0",
      90             :                                                 "Optional customized external boundary id.");
      91         480 :   params.addParam<BoundaryName>(
      92             :       "external_boundary_name", "", "Optional customized external boundary name.");
      93         480 :   params.addParamNamesToGroup(
      94             :       "peripheral_radial_bias peripheral_inner_boundary_layer_width "
      95             :       "peripheral_inner_boundary_layer_intervals peripheral_inner_boundary_layer_bias "
      96             :       "peripheral_outer_boundary_layer_width peripheral_outer_boundary_layer_intervals "
      97             :       "peripheral_outer_boundary_layer_bias",
      98             :       "Mesh Boundary Layer and Biasing Options");
      99         240 :   params.addClassDescription("This PeripheralRingMeshGenerator object adds a circular peripheral "
     100             :                              "region to the input mesh.");
     101             : 
     102         240 :   return params;
     103           0 : }
     104             : 
     105         115 : PeripheralRingMeshGenerator::PeripheralRingMeshGenerator(const InputParameters & parameters)
     106             :   : PolygonMeshGeneratorBase(parameters),
     107           0 :     _input_name(getParam<MeshGeneratorName>("input")),
     108         230 :     _force_input_centroid_as_center(getParam<bool>("force_input_centroid_as_center")),
     109         230 :     _peripheral_layer_num(getParam<unsigned int>("peripheral_layer_num")),
     110         230 :     _peripheral_radial_bias(getParam<Real>("peripheral_radial_bias")),
     111         345 :     _peripheral_inner_boundary_layer_params(
     112         115 :         {getParam<Real>("peripheral_inner_boundary_layer_width"),
     113             :          0.0,
     114         230 :          getParam<Real>("peripheral_inner_boundary_layer_width") > 0.0
     115         242 :              ? getParam<unsigned int>("peripheral_inner_boundary_layer_intervals")
     116             :              : 0,
     117         230 :          getParam<Real>("peripheral_inner_boundary_layer_bias")}),
     118         345 :     _peripheral_outer_boundary_layer_params(
     119         115 :         {getParam<Real>("peripheral_outer_boundary_layer_width"),
     120             :          0.0,
     121         230 :          getParam<Real>("peripheral_outer_boundary_layer_width") > 0.0
     122         242 :              ? getParam<unsigned int>("peripheral_outer_boundary_layer_intervals")
     123             :              : 0,
     124         230 :          getParam<Real>("peripheral_outer_boundary_layer_bias")}),
     125         230 :     _peripheral_ring_radius(getParam<Real>("peripheral_ring_radius")),
     126         230 :     _preserve_volumes(getParam<bool>("preserve_volumes")),
     127         115 :     _input_mesh_external_boundary(getParam<BoundaryName>("input_mesh_external_boundary")),
     128         230 :     _peripheral_ring_block_id(getParam<subdomain_id_type>("peripheral_ring_block_id")),
     129         319 :     _peripheral_ring_block_name(isParamValid("peripheral_ring_block_name")
     130         115 :                                     ? getParam<SubdomainName>("peripheral_ring_block_name")
     131             :                                     : (SubdomainName) ""),
     132         230 :     _external_boundary_id(isParamValid("external_boundary_id")
     133         115 :                               ? getParam<boundary_id_type>("external_boundary_id")
     134             :                               : 0),
     135         115 :     _external_boundary_name(getParam<BoundaryName>("external_boundary_name")),
     136         230 :     _input(getMeshByName(_input_name))
     137             : {
     138         115 :   declareMeshProperty<bool>("hexagon_peripheral_trimmability", false);
     139         115 :   declareMeshProperty<bool>("hexagon_center_trimmability", false);
     140         115 :   declareMeshProperty<bool>("square_peripheral_trimmability", false);
     141         115 :   declareMeshProperty<bool>("square_center_trimmability", false);
     142         115 : }
     143             : 
     144             : std::unique_ptr<MeshBase>
     145         115 : PeripheralRingMeshGenerator::generate()
     146             : {
     147         230 :   if (hasMeshProperty<bool>("hexagon_center_trimmability", _input_name))
     148          18 :     setMeshProperty("hexagon_center_trimmability",
     149             :                     getMeshProperty<bool>("hexagon_center_trimmability", _input_name));
     150         230 :   if (hasMeshProperty<bool>("square_center_trimmability", _input_name))
     151          18 :     setMeshProperty("square_center_trimmability",
     152             :                     getMeshProperty<bool>("square_center_trimmability", _input_name));
     153             : 
     154             :   // Need ReplicatedMesh for stitching
     155         115 :   auto input_mesh = dynamic_cast<ReplicatedMesh *>(_input.get());
     156         115 :   if (!input_mesh)
     157           0 :     paramError("input", "Input is not a replicated mesh, which is required.");
     158         115 :   if (*(input_mesh->elem_dimensions().begin()) != 2 ||
     159         113 :       *(input_mesh->elem_dimensions().rbegin()) != 2)
     160           2 :     paramError("input", "Only 2D meshes are supported.");
     161             : 
     162         113 :   _input_mesh_external_bid =
     163         113 :       MooseMeshUtils::getBoundaryID(_input_mesh_external_boundary, *input_mesh);
     164         113 :   if (!MooseMeshUtils::hasBoundaryName(*input_mesh, _input_mesh_external_boundary))
     165           2 :     paramError("input_mesh_external_boundary",
     166             :                "External boundary does not exist in the input mesh");
     167             :   // We check the element types of input mesh's external boundary here.
     168             :   // We support both linear and quadratic sides (i.e., EDGE2 and EDGE3), but we cannot support mixed
     169             :   // sides
     170         111 :   auto side_list_tmp = input_mesh->get_boundary_info().build_side_list();
     171             :   bool linear_side = false;
     172             :   bool quadratic_side = false;
     173      295311 :   for (const auto & side : side_list_tmp)
     174             :   {
     175      295200 :     if (std::get<2>(side) == _input_mesh_external_bid)
     176             :     {
     177       18368 :       const auto etype = input_mesh->elem_ptr(std::get<0>(side))->side_type(std::get<1>(side));
     178       18368 :       if (etype == EDGE2)
     179             :         linear_side = true;
     180         168 :       else if (etype == EDGE3)
     181             :         quadratic_side = true;
     182             :       else
     183           0 :         paramError("input", "Input contains elements that are not supported by this generator.");
     184             :     }
     185             :   }
     186         111 :   if (linear_side && quadratic_side)
     187           2 :     paramError("input", "Input contains mixed element types on the external boundary.");
     188         109 :   const unsigned int order = linear_side ? 1 : 2;
     189             :   if (order == 2)
     190             :   {
     191           5 :     _peripheral_outer_boundary_layer_params.bias =
     192           5 :         std::sqrt(_peripheral_outer_boundary_layer_params.bias);
     193           5 :     _peripheral_inner_boundary_layer_params.bias =
     194           5 :         std::sqrt(_peripheral_inner_boundary_layer_params.bias);
     195           5 :     _peripheral_outer_boundary_layer_params.intervals *= 2;
     196           5 :     _peripheral_inner_boundary_layer_params.intervals *= 2;
     197           5 :     _peripheral_radial_bias = std::sqrt(_peripheral_radial_bias);
     198             :   }
     199             : 
     200             :   // Move the centroid of the mesh to (0, 0, 0) if input centroid is enforced to be the ring center.
     201         109 :   const Point input_mesh_centroid = MooseMeshUtils::meshCentroidCalculator(*input_mesh);
     202         109 :   if (_force_input_centroid_as_center)
     203           5 :     MeshTools::Modification::translate(
     204             :         *input_mesh, -input_mesh_centroid(0), -input_mesh_centroid(1), -input_mesh_centroid(2));
     205             : 
     206             :   // Use CoM of the input mesh as its origin for azimuthal calculation
     207             :   const Point origin_pt =
     208         109 :       _force_input_centroid_as_center ? Point(0.0, 0.0, 0.0) : input_mesh_centroid;
     209             :   // Vessel for containing maximum radius of the boundary nodes
     210             :   Real max_input_mesh_node_radius;
     211             : 
     212             :   // Calculate biasing terms
     213             :   // For 2nd order mesh, as we need the mid points, the bias factor is square rooted.
     214             :   const auto main_peripheral_bias_terms =
     215         109 :       biasTermsCalculator(_peripheral_radial_bias, _peripheral_layer_num * order);
     216             :   const auto inner_peripheral_bias_terms =
     217             :       biasTermsCalculator(_peripheral_inner_boundary_layer_params.bias,
     218         109 :                           _peripheral_inner_boundary_layer_params.intervals);
     219             :   // It is easier to create outer boundary layer inversely (inwards). Thus, 1.0 / bias is used here.
     220             :   // However, the input parameter definition is not affected.
     221             :   const auto outer_peripheral_bias_terms =
     222         109 :       biasTermsCalculator(1.0 / _peripheral_outer_boundary_layer_params.bias,
     223         109 :                           _peripheral_outer_boundary_layer_params.intervals);
     224             : 
     225         109 :   const unsigned int total_peripheral_layer_num =
     226         109 :       _peripheral_inner_boundary_layer_params.intervals + _peripheral_layer_num * order +
     227         109 :       _peripheral_outer_boundary_layer_params.intervals;
     228             : 
     229             :   // Collect the external boundary nodes of the input mesh using the utility function
     230             :   std::vector<dof_id_type> boundary_ordered_node_list;
     231             :   try
     232             :   {
     233         109 :     FillBetweenPointVectorsTools::isBoundarySimpleClosedLoop(*input_mesh,
     234             :                                                              max_input_mesh_node_radius,
     235             :                                                              boundary_ordered_node_list,
     236             :                                                              origin_pt,
     237         109 :                                                              _input_mesh_external_bid);
     238             :   }
     239           6 :   catch (MooseException & e)
     240             :   {
     241           6 :     if (((std::string)e.what()).compare("The node list provided has more than one segments.") == 0)
     242           2 :       paramError("input_mesh_external_boundary",
     243             :                  "This mesh generator does not work for the provided external boundary as it has "
     244             :                  "more than one segments.");
     245             :     else
     246           4 :       paramError("input_mesh_external_boundary", e.what());
     247           0 :   }
     248             : 
     249         103 :   if (max_input_mesh_node_radius >= _peripheral_ring_radius)
     250           2 :     paramError(
     251             :         "peripheral_ring_radius",
     252             :         "The peripheral ring to be generated must be large enough to cover the entire input mesh.");
     253         101 :   if (!FillBetweenPointVectorsTools::isExternalBoundary(*input_mesh, _input_mesh_external_bid))
     254           2 :     paramError("input_mesh_external_boundary",
     255             :                "The boundary provided is not an external boundary.");
     256             : 
     257             :   std::vector<Point> input_ext_bd_pts;
     258             :   std::vector<Point> output_ext_bd_pts;
     259             :   std::vector<std::tuple<Real, Point, Point>> azi_points;
     260             :   std::vector<Real> azi_array;
     261             :   Real tmp_azi(0.0);
     262          99 :   auto mesh = buildReplicatedMesh(2);
     263             : 
     264             :   // Node counter for the external boundary
     265             :   unsigned int input_ext_node_num = 0;
     266             : 
     267             :   // As the node list collected before is a simple closed loop, the last node is the same as the
     268             :   // first node. We remove it here.
     269             :   boundary_ordered_node_list.pop_back();
     270             :   // Loop over all the boundary nodes
     271             :   // For quadratic sides, the middle points are included automatically
     272       12071 :   for (const auto i : index_range(boundary_ordered_node_list))
     273             :   {
     274       11972 :     input_ext_node_num++;
     275             :     // Define nodes on the inner and outer boundaries of the peripheral region.
     276       11972 :     input_ext_bd_pts.push_back(input_mesh->point(boundary_ordered_node_list[i]));
     277             :     tmp_azi =
     278       11972 :         atan2(input_ext_bd_pts.back()(1) - origin_pt(1), input_ext_bd_pts.back()(0) - origin_pt(0));
     279       11972 :     output_ext_bd_pts.push_back(Point(_peripheral_ring_radius * std::cos(tmp_azi),
     280       11972 :                                       _peripheral_ring_radius * std::sin(tmp_azi),
     281             :                                       origin_pt(2)));
     282             :     // a vector of tuples using azimuthal angle as the key to facilitate sorting
     283             :     azi_points.push_back(
     284       11972 :         std::make_tuple(tmp_azi, input_ext_bd_pts.back(), output_ext_bd_pts.back()));
     285             :     // a simple vector of azimuthal angles for radius correction purposes (in degree)
     286       11972 :     azi_array.push_back(tmp_azi / M_PI * 180.0);
     287             :   }
     288             :   // Only for quadratic sides, if the index of the first node after sorting is even, it is a vertex
     289             :   // node; otherwise, it is a midpoint node.
     290             :   const bool is_first_node_vertex =
     291         104 :       order == 1 ||
     292             :       !(std::distance(azi_array.begin(), std::min_element(azi_array.begin(), azi_array.end())) % 2);
     293          99 :   std::sort(azi_points.begin(), azi_points.end());
     294          99 :   std::sort(azi_array.begin(), azi_array.end());
     295             : 
     296             :   // Angles defined by three neighboring nodes on input mesh's external boundary
     297             :   std::vector<Real> input_bdry_angles;
     298             :   // Normal directions of input boundary nodes
     299             :   std::vector<Point> input_bdry_nd;
     300       12071 :   for (const auto i : index_range(azi_points))
     301             :   {
     302             :     Point p1, p2;
     303             :     const Point pn = Point(0.0, 0.0, 1.0);
     304       11972 :     if (i == 0)
     305             :     {
     306          99 :       p1 = std::get<1>(azi_points[i + 1]) - std::get<1>(azi_points[i]);
     307          99 :       p2 = std::get<1>(azi_points.back()) - std::get<1>(azi_points[i]);
     308             :     }
     309       11873 :     else if (i == azi_points.size() - 1)
     310             :     {
     311          99 :       p1 = std::get<1>(azi_points.front()) - std::get<1>(azi_points.back());
     312          99 :       p2 = std::get<1>(azi_points[i - 1]) - std::get<1>(azi_points.back());
     313             :     }
     314             :     else
     315             :     {
     316       11774 :       p1 = std::get<1>(azi_points[i + 1]) - std::get<1>(azi_points[i]);
     317       11774 :       p2 = std::get<1>(azi_points[i - 1]) - std::get<1>(azi_points[i]);
     318             :     }
     319             :     // Use cross point to get perpendicular direction
     320       11972 :     const Point p1n = (p1.cross(pn)).unit();
     321       11972 :     const Point p2n = -(p2.cross(pn)).unit();
     322       11972 :     Real tmp = p1 * p2 / p1.norm() / p2.norm();
     323             :     // Make sure acos() gets valid input
     324       11972 :     tmp = tmp > 1.0 ? 1.0 : (tmp < -1.0 ? -1.0 : tmp);
     325       11972 :     input_bdry_angles.push_back(acos(tmp) / 2.0);
     326       23944 :     input_bdry_nd.push_back((p1n + p2n).unit());
     327             :   }
     328             : 
     329             :   // 2D vector containing all the node positions of the peripheral region
     330          99 :   std::vector<std::vector<Point>> points_array(total_peripheral_layer_num + 1,
     331          99 :                                                std::vector<Point>(input_ext_node_num));
     332             :   // 2D vector containing all the node ids of the peripheral region
     333             :   std::vector<std::vector<dof_id_type>> node_id_array(total_peripheral_layer_num + 1,
     334          99 :                                                       std::vector<dof_id_type>(input_ext_node_num));
     335             :   // Reference outmost layer of inner boundary layer
     336             :   std::vector<Point> ref_inner_bdry_surf;
     337             :   // First loop
     338       12071 :   for (const auto i : make_range(input_ext_node_num))
     339             :   {
     340             :     // Inner boundary nodes of the peripheral region
     341       11972 :     points_array[0][i] = std::get<1>(azi_points[i]);
     342             :     // Define outer layer of inner boundary layer
     343       11972 :     if (_peripheral_inner_boundary_layer_params.intervals)
     344             :     {
     345             :       // Outside point of the inner boundary layer
     346             :       const Point ref_inner_boundary_shift =
     347        2504 :           (_peripheral_inner_boundary_layer_params.width / sin(input_bdry_angles[i])) *
     348             :           input_bdry_nd[i];
     349        2504 :       ref_inner_bdry_surf.push_back(points_array[0][i] + ref_inner_boundary_shift);
     350             :     }
     351             :   }
     352             : 
     353          99 :   if (_peripheral_inner_boundary_layer_params.intervals)
     354          12 :     innerBdryLayerNodesDefiner(input_ext_node_num,
     355             :                                input_bdry_angles,
     356             :                                ref_inner_bdry_surf,
     357             :                                inner_peripheral_bias_terms,
     358             :                                azi_array,
     359             :                                origin_pt,
     360             :                                points_array);
     361          99 :   const Real correction_factor = _preserve_volumes
     362          99 :                                      ? PolygonalMeshGenerationUtils::radiusCorrectionFactor(
     363             :                                            azi_array, true, order, is_first_node_vertex)
     364             :                                      : 1.0;
     365             :   // Loop to handle outer boundary layer and main region
     366       11483 :   for (const auto i : make_range(input_ext_node_num))
     367             :   {
     368             :     // Outer boundary nodes of the peripheral region
     369       11386 :     points_array[total_peripheral_layer_num][i] = std::get<2>(azi_points[i]) * correction_factor;
     370             :     // Outer boundary layer points
     371       11386 :     if (_peripheral_outer_boundary_layer_params.intervals)
     372             :     {
     373             :       // Inner point of the outer boundary layer
     374             :       const Point outer_boundary_shift =
     375        1918 :           -Point(std::cos(std::get<0>(azi_points[i])), std::sin(std::get<0>(azi_points[i])), 0.0) *
     376             :           _peripheral_outer_boundary_layer_params.width;
     377        1918 :       for (const auto j :
     378       10116 :            make_range(uint(1), _peripheral_outer_boundary_layer_params.intervals + 1))
     379        8198 :         points_array[total_peripheral_layer_num - j][i] =
     380             :             points_array[total_peripheral_layer_num][i] +
     381        8198 :             outer_boundary_shift * outer_peripheral_bias_terms[j - 1];
     382             :     }
     383             :     // Use interpolation to get main region
     384       11386 :     if (MooseUtils::absoluteFuzzyGreaterEqual(
     385       11386 :             (points_array[_peripheral_inner_boundary_layer_params.intervals][i] - origin_pt).norm(),
     386       11386 :             (points_array[_peripheral_inner_boundary_layer_params.intervals +
     387       22772 :                           _peripheral_layer_num * order][i] -
     388             :              origin_pt)
     389       11386 :                 .norm()))
     390             :     {
     391           2 :       paramError("peripheral_inner_boundary_layer_width",
     392             :                  "The summation of peripheral_inner_boundary_layer_width and "
     393             :                  "peripheral_outer_boundary_layer_width must be smaller than the thickness of "
     394             :                  "peripheral ring region.");
     395             :     }
     396             : 
     397       28820 :     for (const auto j : make_range(uint(1), _peripheral_layer_num * order))
     398       17436 :       points_array[j + _peripheral_inner_boundary_layer_params.intervals][i] =
     399             :           points_array[_peripheral_inner_boundary_layer_params.intervals][i] *
     400       17436 :               (1.0 - main_peripheral_bias_terms[j - 1]) +
     401             :           points_array[_peripheral_inner_boundary_layer_params.intervals +
     402             :                        _peripheral_layer_num * order][i] *
     403       17436 :               main_peripheral_bias_terms[j - 1];
     404             :   }
     405          97 :   unsigned int num_total_nodes = (total_peripheral_layer_num + 1) * input_ext_node_num;
     406          97 :   std::vector<Node *> nodes(num_total_nodes); // reserve nodes pointers
     407             :   dof_id_type node_id = 0;
     408             : 
     409             :   // Add nodes to the new mesh
     410         510 :   for (const auto i : make_range(total_peripheral_layer_num + 1))
     411       56793 :     for (const auto j : make_range(input_ext_node_num))
     412             :     {
     413       56380 :       nodes[node_id] = mesh->add_point(points_array[i][j], node_id);
     414       56380 :       node_id_array[i][j] = node_id;
     415       56380 :       node_id++;
     416             :     }
     417             :   // Add elements to the new mesh
     418             :   BoundaryInfo & boundary_info = mesh->get_boundary_info();
     419          97 :   const unsigned int index_shift = is_first_node_vertex ? 0 : 1;
     420         363 :   for (const auto i : make_range(total_peripheral_layer_num / order))
     421       40498 :     for (const auto j : make_range(input_ext_node_num / order))
     422             :     {
     423       40232 :       std::unique_ptr<Elem> new_elem;
     424       40232 :       new_elem = std::make_unique<Quad4>();
     425       40232 :       if (order == 2)
     426             :       {
     427        1600 :         new_elem = std::make_unique<Quad9>();
     428        1600 :         new_elem->set_node(4, nodes[node_id_array[i * order + 1][j * order + index_shift]]);
     429        1600 :         new_elem->set_node(5,
     430        1600 :                            nodes[node_id_array[(i + 1) * order][(j * order + 1 + index_shift) %
     431        1600 :                                                                 input_ext_node_num]]);
     432        1600 :         new_elem->set_node(6,
     433        1600 :                            nodes[node_id_array[i * order + 1][((j + 1) * order + index_shift) %
     434        1600 :                                                               input_ext_node_num]]);
     435        1600 :         new_elem->set_node(
     436        1600 :             7, nodes[node_id_array[i * order][(j * order + 1 + index_shift) % input_ext_node_num]]);
     437        1600 :         new_elem->set_node(8,
     438             :                            nodes[node_id_array[i * order + 1][(j * order + 1 + index_shift) %
     439        1600 :                                                               input_ext_node_num]]);
     440             :       }
     441       40232 :       new_elem->set_node(0, nodes[node_id_array[i * order][j * order + index_shift]]);
     442       40232 :       new_elem->set_node(1, nodes[node_id_array[(i + 1) * order][j * order + index_shift]]);
     443       40232 :       new_elem->set_node(2,
     444       40232 :                          nodes[node_id_array[(i + 1) * order][((j + 1) * order + index_shift) %
     445       40232 :                                                               input_ext_node_num]]);
     446       40232 :       new_elem->set_node(
     447       40232 :           3, nodes[node_id_array[i * order][((j + 1) * order + index_shift) % input_ext_node_num]]);
     448       40232 :       new_elem->subdomain_id() = _peripheral_ring_block_id;
     449             : 
     450       40232 :       Elem * added_elem = mesh->add_elem(std::move(new_elem));
     451             : 
     452       40232 :       if (i == 0)
     453       11188 :         boundary_info.add_side(added_elem, 3, OUTER_SIDESET_ID_ALT);
     454       40232 :       if (i == total_peripheral_layer_num / order - 1)
     455       11188 :         boundary_info.add_side(added_elem, 1, OUTER_SIDESET_ID);
     456       40232 :     }
     457             : 
     458             :   // This would make sure that the boundary OUTER_SIDESET_ID is deleted after stitching.
     459          97 :   if (_input_mesh_external_bid != OUTER_SIDESET_ID)
     460          36 :     MooseMesh::changeBoundaryId(*input_mesh, _input_mesh_external_bid, OUTER_SIDESET_ID, false);
     461          97 :   mesh->prepare_for_use();
     462             :   // Use input_mesh here to retain the subdomain name map
     463          97 :   input_mesh->stitch_meshes(*mesh, OUTER_SIDESET_ID, OUTER_SIDESET_ID_ALT, TOLERANCE, true, false);
     464             : 
     465             :   // Assign subdomain name to the new block if applicable
     466         194 :   if (isParamValid("peripheral_ring_block_name"))
     467          71 :     input_mesh->subdomain_name(_peripheral_ring_block_id) = _peripheral_ring_block_name;
     468             :   // Assign customized external boundary id
     469          97 :   if (_external_boundary_id > 0)
     470           0 :     MooseMesh::changeBoundaryId(*input_mesh, OUTER_SIDESET_ID, _external_boundary_id, false);
     471             :   // Assign customized external boundary name
     472          97 :   if (!_external_boundary_name.empty())
     473             :   {
     474          36 :     input_mesh->get_boundary_info().sideset_name(
     475          36 :         _external_boundary_id > 0 ? _external_boundary_id : (boundary_id_type)OUTER_SIDESET_ID) =
     476          36 :         _external_boundary_name;
     477          36 :     input_mesh->get_boundary_info().nodeset_name(
     478          36 :         _external_boundary_id > 0 ? _external_boundary_id : (boundary_id_type)OUTER_SIDESET_ID) =
     479             :         _external_boundary_name;
     480             :   }
     481             : 
     482          97 :   _input->set_isnt_prepared();
     483         194 :   return dynamic_pointer_cast<MeshBase>(_input);
     484          97 : }
     485             : 
     486             : void
     487          12 : PeripheralRingMeshGenerator::innerBdryLayerNodesDefiner(
     488             :     const unsigned int input_ext_node_num,
     489             :     const std::vector<Real> input_bdry_angles,
     490             :     const std::vector<Point> ref_inner_bdry_surf,
     491             :     const std::vector<Real> inner_peripheral_bias_terms,
     492             :     const std::vector<Real> azi_array,
     493             :     const Point origin_pt,
     494             :     std::vector<std::vector<Point>> & points_array) const
     495             : {
     496             :   // Check if any azimuthal angles are messed after inner boundary layer addition
     497          12 :   std::vector<bool> delete_mark(input_ext_node_num, false);
     498             :   std::vector<Real> interp_azi_data, interp_x_data, interp_y_data;
     499          12 :   std::unique_ptr<LinearInterpolation> linterp_x, linterp_y;
     500             :   // Mark the to-be-deleted elements
     501        2516 :   for (const auto i : make_range(input_ext_node_num))
     502             :   {
     503             :     // For a zig-zag external boundary, when we add a conformal layer onto it by translating the
     504             :     // nodes in the surface normal direction, it is possible that the azimuthal order is flipped.
     505             :     // As shown below, o's are the original boundary nodes, and *'s are the nodes after
     506             :     // translation by the boundary layer thickness.
     507             :     //                         *  *
     508             :     // |               |       | /
     509             :     // o               o-------/--*
     510             :     // |               |     / |
     511             :     // | outside  -->  |   /   |
     512             :     // |               | /     |
     513             :     // o--------o--    o-------o--
     514             :     // To mitigate this flipping issue, we check the node flipping here using the cross product of
     515             :     // neighboring node-to-origin vectors. Flipped nodes are marked and excluded during the
     516             :     // follow-up interpolation.
     517        2504 :     if (!MooseUtils::absoluteFuzzyEqual(input_bdry_angles[i], M_PI / 2.0, 1.0e-4))
     518             :     {
     519             :       unsigned int index_shift = 1;
     520             :       bool minus_shift_flag = true;
     521             :       bool plus_shift_flag = true;
     522             :       // Need to check both directions for multiple shifts
     523             :       // Half of the external boundary nodes are used as an upper limit to avert infinite loops
     524         332 :       while (index_shift < input_ext_node_num / 2 && (minus_shift_flag || plus_shift_flag))
     525             :       {
     526         166 :         if (((ref_inner_bdry_surf[MathUtils::euclideanMod(((int)i - (int)index_shift),
     527         166 :                                                           (int)input_ext_node_num)] -
     528             :               origin_pt)
     529         166 :                  .cross(ref_inner_bdry_surf[i] - origin_pt))(2) <= 0.0 &&
     530             :             minus_shift_flag)
     531             :           delete_mark[MathUtils::euclideanMod(((int)i - (int)index_shift),
     532             :                                               (int)input_ext_node_num)] = true;
     533             :         else
     534             :           minus_shift_flag = false;
     535         166 :         if (((ref_inner_bdry_surf[(i + index_shift) % input_ext_node_num] - origin_pt)
     536         166 :                  .cross(ref_inner_bdry_surf[i] - origin_pt))(2) >= 0.0 &&
     537             :             plus_shift_flag)
     538             :           delete_mark[(i + index_shift) % input_ext_node_num] = true;
     539             :         else
     540             :           plus_shift_flag = false;
     541         166 :         index_shift++;
     542             :       }
     543             :     }
     544             :   }
     545             :   // Create vectors for interpolation
     546             :   // Due to the flip issue, linear interpolation is used here to mark the location of the boundary
     547             :   // layer's outer boundary.
     548        2516 :   for (const auto i : make_range(input_ext_node_num))
     549             :   {
     550        2504 :     if (!delete_mark[i])
     551             :     {
     552        2504 :       interp_azi_data.push_back(atan2(ref_inner_bdry_surf[i](1) - origin_pt(1),
     553        2504 :                                       ref_inner_bdry_surf[i](0) - origin_pt(0)));
     554        2504 :       interp_x_data.push_back(ref_inner_bdry_surf[i](0));
     555        2504 :       interp_y_data.push_back(ref_inner_bdry_surf[i](1));
     556        2504 :       if (interp_azi_data.size() > 1)
     557        2492 :         if (interp_azi_data.back() < interp_azi_data[interp_azi_data.size() - 2])
     558           0 :           interp_azi_data.back() += 2.0 * M_PI;
     559             :     }
     560             :   }
     561          12 :   const Real interp_x0 = interp_x_data.front();
     562          12 :   const Real interp_xt = interp_x_data.back();
     563          12 :   const Real interp_y0 = interp_y_data.front();
     564          12 :   const Real interp_yt = interp_y_data.back();
     565          12 :   const Real incept_azi0 = interp_azi_data.front();
     566          12 :   const Real incept_azit = interp_azi_data.back();
     567             : 
     568          12 :   if (MooseUtils::absoluteFuzzyGreaterThan(incept_azi0, -M_PI))
     569             :   {
     570           5 :     interp_azi_data.insert(interp_azi_data.begin(), incept_azit - 2.0 * M_PI);
     571           5 :     interp_x_data.insert(interp_x_data.begin(), interp_xt);
     572           5 :     interp_y_data.insert(interp_y_data.begin(), interp_yt);
     573             :   }
     574             :   else
     575           7 :     interp_azi_data.front() = -M_PI;
     576          12 :   if (MooseUtils::absoluteFuzzyLessThan(incept_azit, M_PI))
     577             :   {
     578           7 :     interp_azi_data.push_back(incept_azi0 + 2 * M_PI);
     579           7 :     interp_x_data.push_back(interp_x0);
     580           7 :     interp_y_data.push_back(interp_y0);
     581             :   }
     582             :   else
     583           5 :     interp_azi_data.back() = M_PI;
     584             : 
     585             :   // Establish interpolation
     586          24 :   linterp_x = std::make_unique<LinearInterpolation>(interp_azi_data, interp_x_data);
     587          24 :   linterp_y = std::make_unique<LinearInterpolation>(interp_azi_data, interp_y_data);
     588             :   //  Loop to handle inner boundary layer
     589        2516 :   for (const auto i : make_range(input_ext_node_num))
     590             :   {
     591             :     // Outside point of the inner boundary layer
     592             :     // Using interpolation, the azimuthal angles do not need to be changed.
     593        2504 :     const Point inner_boundary_shift = Point(linterp_x->sample(azi_array[i] / 180.0 * M_PI),
     594        2504 :                                              linterp_y->sample(azi_array[i] / 180.0 * M_PI),
     595             :                                              origin_pt(2)) -
     596        2504 :                                        points_array[0][i];
     597       11288 :     for (const auto j : make_range(uint(1), _peripheral_inner_boundary_layer_params.intervals + 1))
     598        8784 :       points_array[j][i] =
     599        8784 :           points_array[0][i] + inner_boundary_shift * inner_peripheral_bias_terms[j - 1];
     600             :   }
     601          24 : }

Generated by: LCOV version 1.14