LCOV - code coverage report
Current view: top level - src/meshgenerators - AdvancedConcentricCircleGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose reactor: #31405 (292dce) with base fef103 Lines: 141 151 93.4 %
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 "AdvancedConcentricCircleGenerator.h"
      11             : #include "PolygonalMeshGenerationUtils.h"
      12             : 
      13             : // C++ includes
      14             : #include <cmath>
      15             : 
      16             : registerMooseObject("ReactorApp", AdvancedConcentricCircleGenerator);
      17             : 
      18             : InputParameters
      19         816 : AdvancedConcentricCircleGenerator::validParams()
      20             : {
      21         816 :   InputParameters params = ConcentricCircleGeneratorBase::validParams();
      22             : 
      23         816 :   params.makeParamRequired<std::vector<Real>>("ring_radii");
      24         816 :   params.makeParamRequired<std::vector<unsigned int>>("ring_intervals");
      25             : 
      26        1632 :   params.addRangeCheckedParam<unsigned int>(
      27             :       "num_sectors",
      28             :       "num_sectors>2",
      29             :       "Number of azimuthal sectors of the circular mesh to be generated.");
      30        1632 :   params.addRangeCheckedParam<std::vector<Real>>(
      31             :       "customized_azimuthal_angles",
      32             :       "customized_azimuthal_angles>=0&customized_azimuthal_angles<360",
      33             :       "List of the user-specified azimuthal angles of the nodes.");
      34             : 
      35        1632 :   params.addParamNamesToGroup("num_sectors customized_azimuthal_angles", "Azimuthal Mesh Density");
      36             : 
      37         816 :   params.addClassDescription("This AdvancedConcentricCircleGenerator object is designed to mesh a "
      38             :                              "concentric circular geometry.");
      39             : 
      40         816 :   return params;
      41           0 : }
      42             : 
      43         421 : AdvancedConcentricCircleGenerator::AdvancedConcentricCircleGenerator(
      44         421 :     const InputParameters & parameters)
      45             :   : ConcentricCircleGeneratorBase(parameters),
      46         581 :     _azimuthal_angles(isParamValid("customized_azimuthal_angles")
      47         409 :                           ? getParam<std::vector<Real>>("customized_azimuthal_angles")
      48             :                           : std::vector<Real>()),
      49        1464 :     _num_sectors(isParamValid("num_sectors") ? getParam<unsigned int>("num_sectors")
      50         421 :                                              : _azimuthal_angles.size())
      51             : {
      52         409 :   const unsigned short tri_order = _tri_elem_type == TRI_ELEM_TYPE::TRI3 ? 1 : 2;
      53         409 :   const unsigned short quad_order = _quad_elem_type == QUAD_ELEM_TYPE::QUAD4 ? 1 : 2;
      54             :   // 1. If the generated mesh has only one ring layer of triangular elements, then no
      55             :   // quad elements are generated;
      56             :   // 2. Otherwise, both types of elements are generated.
      57         409 :   _order = tri_order;
      58         134 :   if (_ring_radii.size() == 1 && _ring_intervals.front() == 1 &&
      59         543 :       _ring_inner_boundary_layer_params.intervals.front() == 0 &&
      60         134 :       _ring_outer_boundary_layer_params.intervals.front() == 0)
      61             :   {
      62         134 :     if (tri_order != quad_order)
      63          36 :       _quad_elem_type = tri_order == 1 ? QUAD_ELEM_TYPE::QUAD4 : QUAD_ELEM_TYPE::QUAD9;
      64             :   }
      65         275 :   else if (tri_order != quad_order)
      66           2 :     paramError("tri_element_type",
      67             :                "the element types of triangular and quadrilateral elements must be compatible if "
      68             :                "both types of elements are generated.");
      69             : 
      70         407 :   if (_num_sectors == 0)
      71           2 :     paramError(
      72             :         "num_sectors",
      73             :         "this parameter must be specified if 'customized_azimuthal_angles' is not provided.");
      74         405 :   if (_azimuthal_angles.empty())
      75             :   {
      76        3130 :     for (unsigned int i = 0; i < _num_sectors; i++)
      77             :     {
      78        2811 :       _azimuthal_angles.push_back((Real)i * 360.0 / (Real)_num_sectors);
      79        2811 :       _virtual_nums_sectors.push_back((Real)_num_sectors);
      80             :     }
      81             :   }
      82             :   else
      83             :   {
      84          86 :     if (_num_sectors != _azimuthal_angles.size())
      85           2 :       paramError("num_sectors",
      86             :                  "this parameter must be equal to the size of 'customized_azimuthal_angles' if "
      87             :                  "both parameters are provided.");
      88        7437 :     for (unsigned int i = 0; i < _azimuthal_angles.size(); i++)
      89             :     {
      90        7357 :       const Real azi_angle_interval = i == _azimuthal_angles.size() - 1
      91        7357 :                                           ? 360.0 + _azimuthal_angles[0] - _azimuthal_angles[i]
      92        7277 :                                           : _azimuthal_angles[i + 1] - _azimuthal_angles[i];
      93        7357 :       if (azi_angle_interval <= 0.0)
      94           2 :         paramError("customized_azimuthal_angles",
      95             :                    "the azimuthal angles provided must be strictly increasing.");
      96        7355 :       else if (azi_angle_interval >= 120.0)
      97           2 :         paramError("customized_azimuthal_angles",
      98             :                    "please make sure the circle azimuthal discretization angles are less than "
      99             :                    "120.0 to avert awkward polygonization.");
     100        7353 :       _virtual_nums_sectors.push_back(360.0 / azi_angle_interval);
     101             :     }
     102             :   }
     103             :   // Customized interface boundary id/name related error messages
     104         399 :   if (_inward_interface_boundary_names.size() > 0 &&
     105           2 :       _inward_interface_boundary_names.size() != _ring_radii.size() - 1)
     106           2 :     paramError("inward_interface_boundary_names",
     107             :                "If provided, the length of this parameter must be identical to the total number of "
     108             :                "interfaces.");
     109         397 :   if (_outward_interface_boundary_names.size() > 0 &&
     110           2 :       _outward_interface_boundary_names.size() != _ring_radii.size() - 1)
     111           2 :     paramError("outward_interface_boundary_names",
     112             :                "If provided, the length of this parameter must be identical to the total number of "
     113             :                "interfaces.");
     114             : 
     115             :   const unsigned int num_innermost_ring_layers =
     116         395 :       _ring_inner_boundary_layer_params.intervals.front() + _ring_intervals.front() +
     117         395 :       _ring_outer_boundary_layer_params.intervals.front();
     118         395 :   if (!_ring_block_ids.empty() &&
     119             :       _ring_block_ids.size() !=
     120         348 :           (_ring_intervals.size() + (unsigned int)(num_innermost_ring_layers != 1)))
     121           0 :     paramError("ring_block_ids",
     122             :                "This parameter must have the appropriate size if it is provided. The size should "
     123             :                "be the same as the size of 'ring_intervals' if the innermost ring interval "
     124             :                "(including boundary layers) is unity; otherwise the size should be greater than "
     125             :                "the size of 'ring_intervals' by one. If 'quad_center_elements' is true, it is "
     126             :                "optional to only provide this parameter with the same size as 'ring_intervals'");
     127         395 :   if (!_ring_block_names.empty() &&
     128             :       _ring_block_names.size() !=
     129         348 :           (_ring_intervals.size() + (unsigned int)(num_innermost_ring_layers != 1)))
     130           0 :     paramError("ring_block_names",
     131             :                "This parameter must have the appropriate size if it is set. The size should be the "
     132             :                "same as the size of 'ring_intervals' if the innermost ring interval (including "
     133             :                "boundary layers) is unity; otherwise the size should be greater than the size of "
     134             :                "'ring_intervals' by one. If 'quad_center_elements' is true, it is optional to only "
     135             :                "provide this parameter with the same size as 'ring_intervals'");
     136        1051 :   for (unsigned int i = 0; i < _ring_radii.size(); i++)
     137             :   {
     138         656 :     const Real layer_width = _ring_radii[i] - (i == 0 ? 0.0 : _ring_radii[i - 1]);
     139         656 :     _ring_inner_boundary_layer_params.fractions.push_back(
     140         656 :         _ring_inner_boundary_layer_params.widths[i] / layer_width);
     141         656 :     _ring_outer_boundary_layer_params.fractions.push_back(
     142         656 :         _ring_outer_boundary_layer_params.widths[i] / layer_width);
     143             :   }
     144        1051 :   for (unsigned int i = 0; i < _ring_inner_boundary_layer_params.fractions.size(); i++)
     145         656 :     if (MooseUtils::absoluteFuzzyEqual(_ring_inner_boundary_layer_params.fractions[i], 0.0) &&
     146         656 :         _ring_inner_boundary_layer_params.intervals[i] > 0)
     147           0 :       paramError("ring_inner_boundary_layer_intervals",
     148             :                  "Ring inner boundary layer must have zero interval if its thickness is zero.");
     149             :     else if (MooseUtils::absoluteFuzzyGreaterThan(_ring_inner_boundary_layer_params.fractions[i],
     150         656 :                                                   0.0) &&
     151           0 :              _ring_inner_boundary_layer_params.intervals[i] == 0)
     152           0 :       paramError(
     153             :           "ring_inner_boundary_layer_intervals",
     154             :           "Ring inner boundary layer must have non-zero interval if its thickness is not zero.");
     155        1051 :   for (unsigned int i = 0; i < _ring_outer_boundary_layer_params.fractions.size(); i++)
     156             :   {
     157         656 :     if (MooseUtils::absoluteFuzzyEqual(_ring_outer_boundary_layer_params.fractions[i], 0.0) &&
     158         656 :         _ring_outer_boundary_layer_params.intervals[i] > 0)
     159           0 :       paramError("ring_outer_boundary_layer_intervals",
     160             :                  "Ring outer boundary layer must have zero interval if its thickness is zero.");
     161             :     else if (MooseUtils::absoluteFuzzyGreaterThan(_ring_outer_boundary_layer_params.fractions[i],
     162         656 :                                                   0.0) &&
     163           0 :              _ring_outer_boundary_layer_params.intervals[i] == 0)
     164           0 :       paramError(
     165             :           "ring_outer_boundary_layer_intervals",
     166             :           "Ring outer boundary layer must have non-zero interval if its thickness is not zero.");
     167         656 :     if (_ring_inner_boundary_layer_params.fractions[i] +
     168             :             _ring_outer_boundary_layer_params.fractions[i] >=
     169             :         1.0)
     170           0 :       paramError("ring_inner_boundary_layer_widths",
     171             :                  "Summation of ring_inner_boundary_layer_widths and "
     172             :                  "ring_outer_boundary_layer_widths cannot exceeds the ring layer width.");
     173             :   }
     174             : 
     175        1051 :   for (unsigned int i = 0; i < _ring_radii.size(); i++)
     176             :   {
     177         656 :     const Real layer_width = _ring_radii[i] - (i == 0 ? 0.0 : _ring_radii[i - 1]);
     178         656 :     _ring_inner_boundary_layer_params.fractions.push_back(
     179         656 :         _ring_inner_boundary_layer_params.widths[i] / layer_width);
     180         656 :     _ring_outer_boundary_layer_params.fractions.push_back(
     181         656 :         _ring_outer_boundary_layer_params.widths[i] / layer_width);
     182             :   }
     183         395 : }
     184             : 
     185             : std::unique_ptr<MeshBase>
     186         319 : AdvancedConcentricCircleGenerator::generate()
     187             : {
     188             :   std::vector<Real> ring_radii_corr;
     189             :   std::vector<Real> mod_azimuthal_angles;
     190             : 
     191        9434 :   for (unsigned int i = 1; i < _azimuthal_angles.size(); i++)
     192             :   {
     193        9115 :     mod_azimuthal_angles.push_back(_azimuthal_angles[i - 1]);
     194        9115 :     if (_order == 2)
     195         297 :       mod_azimuthal_angles.push_back((_azimuthal_angles[i - 1] + _azimuthal_angles[i]) / 2.0);
     196             :   }
     197         319 :   mod_azimuthal_angles.push_back(_azimuthal_angles.back());
     198         319 :   if (_order == 2)
     199          36 :     mod_azimuthal_angles.push_back((_azimuthal_angles.back() + _azimuthal_angles.front() + 360.0) /
     200             :                                    2.0);
     201             : 
     202             :   const Real corr_factor =
     203         319 :       _preserve_volumes
     204         319 :           ? PolygonalMeshGenerationUtils::radiusCorrectionFactor(mod_azimuthal_angles, true, _order)
     205             :           : 1.0;
     206             : 
     207         849 :   for (const auto & ring_radius : _ring_radii)
     208         530 :     ring_radii_corr.push_back(ring_radius * corr_factor);
     209             : 
     210             :   const multiBdryLayerParams empty_params = {
     211             :       std::vector<Real>(), std::vector<Real>(), std::vector<unsigned int>(), std::vector<Real>()};
     212         319 :   const singleBdryLayerParams empty_param = {0.0, 0.0, 0, 1.0};
     213             : 
     214             :   // A dummy pitch number is needed for callind buildSlice()
     215             :   // Any value larger than twice of the largest ring radius will work
     216         319 :   const Real dummy_pitch = _ring_radii.back() * 3.0;
     217             :   const unsigned int num_sectors_per_side = 1;
     218             :   const unsigned int background_intervals = 0;
     219             :   const Real background_radial_bias = 1.0;
     220             : 
     221             :   dof_id_type node_id_background_meta;
     222             : 
     223             :   auto mesh = buildSlice(ring_radii_corr,
     224         319 :                          _ring_intervals,
     225         319 :                          _ring_radial_biases,
     226         319 :                          _ring_inner_boundary_layer_params,
     227         319 :                          _ring_outer_boundary_layer_params,
     228         638 :                          std::vector<Real>(),
     229         319 :                          std::vector<unsigned int>(),
     230         319 :                          std::vector<Real>(),
     231             :                          empty_params,
     232             :                          empty_params,
     233             :                          dummy_pitch,
     234             :                          num_sectors_per_side,
     235             :                          background_intervals,
     236             :                          background_radial_bias,
     237             :                          empty_param,
     238             :                          empty_param,
     239             :                          node_id_background_meta,
     240             :                          _virtual_nums_sectors[0],
     241             :                          /*side_index*/ 1,
     242         319 :                          std::vector<Real>(),
     243         319 :                          _block_id_shift,
     244             :                          /* quad_center_elements */ false,
     245             :                          /* center_quad_factor */ 0.0,
     246         319 :                          _create_inward_interface_boundaries,
     247         319 :                          _create_outward_interface_boundaries,
     248         319 :                          _interface_boundary_id_shift,
     249             :                          1.0,
     250             :                          true,
     251             :                          _tri_elem_type,
     252         319 :                          _quad_elem_type);
     253         319 :   MeshTools::Modification::rotate(*mesh, -_azimuthal_angles[0], 0, 0);
     254             : 
     255        9434 :   for (unsigned int i = 1; i < _num_sectors; i++)
     256             :   {
     257             :     auto mesh_tmp = buildSlice(ring_radii_corr,
     258             :                                _ring_intervals,
     259             :                                _ring_radial_biases,
     260             :                                _ring_inner_boundary_layer_params,
     261             :                                _ring_outer_boundary_layer_params,
     262       18230 :                                std::vector<Real>(),
     263        9115 :                                std::vector<unsigned int>(),
     264        9115 :                                std::vector<Real>(),
     265             :                                empty_params,
     266             :                                empty_params,
     267             :                                dummy_pitch,
     268             :                                num_sectors_per_side,
     269             :                                background_intervals,
     270             :                                background_radial_bias,
     271             :                                empty_param,
     272             :                                empty_param,
     273             :                                node_id_background_meta,
     274        9115 :                                _virtual_nums_sectors[i],
     275             :                                /*side_index*/ i + 1,
     276        9115 :                                std::vector<Real>(),
     277        9115 :                                _block_id_shift,
     278             :                                /* quad_center_elements */ false,
     279             :                                /* center_quad_factor */ 0.0,
     280        9115 :                                _create_inward_interface_boundaries,
     281        9115 :                                _create_outward_interface_boundaries,
     282        9115 :                                _interface_boundary_id_shift,
     283             :                                1.0,
     284             :                                true,
     285             :                                _tri_elem_type,
     286        9115 :                                _quad_elem_type);
     287             : 
     288        9115 :     ReplicatedMesh other_mesh(*mesh_tmp);
     289        9115 :     MeshTools::Modification::rotate(other_mesh, -_azimuthal_angles[i], 0, 0);
     290        9115 :     mesh->prepare_for_use();
     291        9115 :     other_mesh.prepare_for_use();
     292             :     // As we rotate the mesh in the negative direction (see "-" before _azimuthal_angles[i]),
     293             :     // the order of SLICE_END and SLICE_BEGIN should be reversed compared to the similar call in
     294             :     // PolygonConcentricCircleMeshGeneratorBase.C
     295        9115 :     mesh->stitch_meshes(other_mesh, SLICE_END, SLICE_BEGIN, TOLERANCE, true, false);
     296        9115 :     other_mesh.clear();
     297        9115 :   }
     298             : 
     299         319 :   if (!_generate_side_specific_boundaries)
     300        9753 :     for (unsigned int i = 0; i < _num_sectors; i++)
     301        9434 :       mesh->get_boundary_info().remove_id(i + 1 + OUTER_SIDESET_ID_ALT);
     302             : 
     303             :   // An extra step to stich the first and last slices together
     304         319 :   mesh->stitch_surfaces(SLICE_END, SLICE_BEGIN, TOLERANCE, true, false);
     305             : 
     306         319 :   mesh->prepare_for_use();
     307             : 
     308             :   // Set up customized Block Names and/or IDs
     309         319 :   unsigned int block_it = 0;
     310         319 :   unsigned int ring_block_num = 0;
     311             :   std::vector<subdomain_id_type> block_ids_old;
     312             :   std::vector<subdomain_id_type> block_ids_new;
     313             :   std::vector<SubdomainName> block_names;
     314             : 
     315         319 :   ringBlockIdsNamesPreparer(block_it, ring_block_num, block_ids_old, block_ids_new, block_names);
     316             : 
     317         319 :   assignBlockIdsNames(
     318             :       *mesh, block_ids_old, block_ids_new, block_names, "AdvancedConcentricCircleGenerator");
     319             : 
     320             :   // Customized boundary ids and names
     321         319 :   if (_external_boundary_id > 0)
     322         158 :     MooseMesh::changeBoundaryId(*mesh, OUTER_SIDESET_ID, _external_boundary_id, true);
     323             :   else
     324         161 :     MooseMesh::changeBoundaryId(*mesh, OUTER_SIDESET_ID, 0, true);
     325         319 :   if (!_external_boundary_name.empty())
     326             :   {
     327         158 :     mesh->get_boundary_info().sideset_name(_external_boundary_id > 0 ? _external_boundary_id : 0) =
     328         158 :         _external_boundary_name;
     329         158 :     mesh->get_boundary_info().nodeset_name(_external_boundary_id > 0 ? _external_boundary_id : 0) =
     330             :         _external_boundary_name;
     331             :   }
     332             : 
     333         319 :   assignInterfaceBoundaryNames(*mesh);
     334             : 
     335             :   mesh->set_isnt_prepared();
     336         638 :   return dynamic_pointer_cast<MeshBase>(mesh);
     337         319 : }

Generated by: LCOV version 1.14