LCOV - code coverage report
Current view: top level - src/meshgenerators - AzimuthalBlockSplitGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose reactor: #32971 (54bef8) with base c6cf66 Lines: 260 277 93.9 %
Date: 2026-05-29 20:39:24 Functions: 7 7 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 "AzimuthalBlockSplitGenerator.h"
      11             : #include "MooseMeshUtils.h"
      12             : #include "PolygonalMeshGenerationUtils.h"
      13             : 
      14             : // C++ includes
      15             : #include <cmath> // provides round, not std::round (see http://www.cplusplus.com/reference/cmath/round/)
      16             : 
      17             : registerMooseObject("ReactorApp", AzimuthalBlockSplitGenerator);
      18             : 
      19             : InputParameters
      20         104 : AzimuthalBlockSplitGenerator::validParams()
      21             : {
      22         104 :   InputParameters params = PolygonMeshGeneratorBase::validParams();
      23         208 :   params.addRequiredParam<MeshGeneratorName>("input", "The input mesh to be modified.");
      24         208 :   params.addRequiredParam<std::vector<SubdomainName>>(
      25             :       "old_blocks", "The list of blocks in the input mesh that need to be modified.");
      26         208 :   params.addRequiredParam<std::vector<subdomain_id_type>>(
      27             :       "new_block_ids", "The block IDs to be used for the new selected azimuthal angle blocks.");
      28         208 :   params.addParam<std::vector<SubdomainName>>(
      29             :       "new_block_names",
      30             :       {},
      31             :       "The optional block names to be used for the new selected azimulathal angle blocks.");
      32         208 :   params.addParam<bool>("preserve_volumes",
      33         208 :                         true,
      34             :                         "Volume of concentric circles can be preserved using this function.");
      35         208 :   params.addRequiredRangeCheckedParam<Real>("start_angle",
      36             :                                             "start_angle>=0.0 & start_angle<360.0",
      37             :                                             "Starting azimuthal angle of the new block.");
      38         208 :   params.addRequiredRangeCheckedParam<Real>("angle_range",
      39             :                                             "angle_range>0.0 & angle_range<=180.0",
      40             :                                             "Azimuthal angle range of the new block.");
      41         104 :   params.addClassDescription(
      42             :       "This AzimuthalBlockSplitGenerator object takes in a polygon/hexagon concentric circle mesh "
      43             :       "and renames blocks on a user-defined azimuthal segment / wedge of the mesh.");
      44             : 
      45         104 :   return params;
      46           0 : }
      47             : 
      48          53 : AzimuthalBlockSplitGenerator::AzimuthalBlockSplitGenerator(const InputParameters & parameters)
      49             :   : PolygonMeshGeneratorBase(parameters),
      50          53 :     _input_name(getParam<MeshGeneratorName>("input")),
      51         106 :     _new_block_ids(getParam<std::vector<subdomain_id_type>>("new_block_ids")),
      52         106 :     _new_block_names(getParam<std::vector<SubdomainName>>("new_block_names")),
      53         106 :     _preserve_volumes(getParam<bool>("preserve_volumes")),
      54         106 :     _start_angle(getParam<Real>("start_angle") + 90.0),
      55         106 :     _angle_range(getParam<Real>("angle_range")),
      56          53 :     _azimuthal_angle_meta(
      57         106 :         declareMeshProperty<std::vector<Real>>("azimuthal_angle_meta", std::vector<Real>())),
      58         106 :     _input(getMeshByName(_input_name))
      59             : {
      60          53 :   declareMeshProperty<Real>("pattern_pitch_meta", 0.0);
      61         106 :   declareMeshProperty<bool>("is_control_drum_meta", true);
      62          53 :   if (!_new_block_names.empty() && _new_block_names.size() != _new_block_ids.size())
      63           2 :     paramError("new_block_names",
      64             :                "This parameter, if provided, must have the same size as new_block_ids.");
      65             :   _num_sectors_per_side_meta =
      66          51 :       getMeshProperty<std::vector<unsigned int>>("num_sectors_per_side_meta", _input_name);
      67          51 :   _start_angle = _start_angle >= 360.0 ? _start_angle - 360.0 : _start_angle;
      68          51 :   _end_angle = (_start_angle + _angle_range >= 360.0) ? (_start_angle + _angle_range - 360.0)
      69             :                                                       : (_start_angle + _angle_range);
      70          51 : }
      71             : 
      72             : std::unique_ptr<MeshBase>
      73          51 : AzimuthalBlockSplitGenerator::generate()
      74             : {
      75          51 :   auto replicated_mesh_ptr = dynamic_cast<ReplicatedMesh *>(_input.get());
      76          51 :   if (!replicated_mesh_ptr)
      77           0 :     paramError("input", "Input is not a replicated mesh, which is required");
      78             : 
      79             :   ReplicatedMesh & mesh = *replicated_mesh_ptr;
      80             : 
      81             :   // We'll be querying the mesh for subdomain ids
      82          51 :   if (!mesh.preparation().has_cached_elem_data)
      83          51 :     mesh.cache_elem_data();
      84             : 
      85             :   // Check the order of the input mesh's elements
      86             :   // Meanwhile, record the vertex average of each element for future comparison
      87             :   unsigned short order = 0;
      88             :   std::map<dof_id_type, Point> vertex_avgs;
      89        5312 :   for (const auto & elem : mesh.element_ptr_range())
      90             :   {
      91        5214 :     switch (elem->type())
      92             :     {
      93        4426 :       case TRI3:
      94             :       case QUAD4:
      95        4426 :         if (order == 2)
      96           0 :           paramError("input", "This mesh contains elements of different orders.");
      97             :         order = 1;
      98             :         break;
      99         786 :       case TRI6:
     100             :       case TRI7:
     101             :       case QUAD8:
     102             :       case QUAD9:
     103         786 :         if (order == 1)
     104           2 :           paramError("input", "This mesh contains elements of different orders.");
     105             :         order = 2;
     106             :         break;
     107           2 :       default:
     108           2 :         paramError("input", "This mesh contains elements of unsupported type.");
     109             :     }
     110        5210 :     vertex_avgs[elem->id()] = elem->vertex_average();
     111          47 :   }
     112             : 
     113             :   _old_block_ids =
     114         141 :       MooseMeshUtils::getSubdomainIDs(mesh, getParam<std::vector<SubdomainName>>("old_blocks"));
     115          47 :   if (_old_block_ids.size() != _new_block_ids.size())
     116           4 :     paramError("new_block_ids", "This parameter must have the same size as old_blocks.");
     117             : 
     118             :   // Check that the block ids/names exist in the mesh
     119             :   std::set<SubdomainID> mesh_blocks;
     120          43 :   mesh.subdomain_ids(mesh_blocks);
     121             : 
     122         154 :   for (unsigned int i = 0; i < _old_block_ids.size(); i++)
     123         113 :     if (_old_block_ids[i] == Moose::INVALID_BLOCK_ID || !mesh_blocks.count(_old_block_ids[i]))
     124           2 :       paramError("old_blocks",
     125             :                  "This parameter contains blocks that do not exist in the input mesh. Error "
     126           2 :                  "triggered by block: " +
     127           2 :                      getParam<std::vector<SubdomainName>>("old_blocks")[i]);
     128             : 
     129          41 :   if (std::find(_old_block_ids.begin(),
     130             :                 _old_block_ids.end(),
     131             :                 getMeshProperty<subdomain_id_type>("quad_center_block_id", _input_name)) !=
     132             :       _old_block_ids.end())
     133           2 :     paramError(
     134             :         "old_blocks",
     135             :         "This parameter contains a block that involves center quad elements, azimuthal splitting "
     136             :         "is currently not supported in this case.");
     137             : 
     138          39 :   MeshTools::Modification::rotate(mesh, 90.0, 0.0, 0.0);
     139          39 :   _azimuthal_angle_meta = azimuthalAnglesCollector(mesh, -180.0, 180.0, ANGLE_DEGREE);
     140          35 :   std::vector<Real> azimuthal_angles_vtx;
     141             :   const bool is_first_value_vertex =
     142          39 :       order == 1 ? true : MooseUtils::absoluteFuzzyEqual(_azimuthal_angle_meta[0], -180.0);
     143             :   if (order == 1)
     144          32 :     azimuthal_angles_vtx = _azimuthal_angle_meta;
     145             :   else
     146         399 :     for (const auto & i : make_range(_azimuthal_angle_meta.size()))
     147         392 :       if (i % 2 != is_first_value_vertex)
     148         196 :         azimuthal_angles_vtx.push_back(_azimuthal_angle_meta[i]);
     149             : 
     150             :   // So that this class works for both derived classes of PolygonMeshGeneratorBase
     151          78 :   auto pattern_pitch_meta = std::max(getMeshProperty<Real>("pitch_meta", _input_name),
     152          39 :                                      getMeshProperty<Real>("pattern_pitch_meta", _input_name));
     153          39 :   setMeshProperty("pattern_pitch_meta", pattern_pitch_meta);
     154             : 
     155             :   Real radiusCorrectionFactor_original =
     156          39 :       _preserve_volumes ? PolygonalMeshGenerationUtils::radiusCorrectionFactor(
     157          39 :                               _azimuthal_angle_meta, true, order, is_first_value_vertex)
     158             :                         : 1.0;
     159             : 
     160          39 :   const Real azi_tol = 1.0E-6;
     161          39 :   const Real rad_tol = 1.0e-6;
     162          39 :   const Real side_angular_range = 360.0 / (Real)_num_sectors_per_side_meta.size();
     163             :   const Real side_angular_shift =
     164          39 :       _num_sectors_per_side_meta.size() % 2 == 0 ? 0.0 : side_angular_range / 2.0;
     165             : 
     166          39 :   _start_angle = _start_angle > 180.0 ? _start_angle - 360.0 : _start_angle;
     167          39 :   _end_angle = _end_angle > 180.0 ? _end_angle - 360.0 : _end_angle;
     168             : 
     169             :   Real original_start_down;
     170          39 :   std::vector<Real>::iterator original_start_down_it;
     171             :   Real original_start_up;
     172          39 :   std::vector<Real>::iterator original_start_up_it;
     173             : 
     174             :   // Identify the mesh azimuthal angles of the elements that need to be modified for the starting
     175             :   // edge of the absorber region.
     176          39 :   angleIdentifier(_start_angle,
     177             :                   original_start_down,
     178             :                   original_start_down_it,
     179             :                   original_start_up,
     180             :                   original_start_up_it,
     181             :                   azimuthal_angles_vtx);
     182             : 
     183             :   Real original_end_down;
     184          39 :   std::vector<Real>::iterator original_end_down_it;
     185             :   Real original_end_up;
     186          39 :   std::vector<Real>::iterator original_end_up_it;
     187             : 
     188             :   // Identify the mesh azimuthal angles of the elements that need to be modified for the ending
     189             :   // edge of the absorber region.
     190          39 :   angleIdentifier(_end_angle,
     191             :                   original_end_down,
     192             :                   original_end_down_it,
     193             :                   original_end_up,
     194             :                   original_end_up_it,
     195             :                   azimuthal_angles_vtx);
     196             : 
     197             :   Real azi_to_mod_start;
     198             :   Real azi_to_keep_start;
     199             : 
     200             :   // For the two mesh azimuthal angles identified, determine which need to be moved to the starting
     201             :   // edge position.
     202          39 :   angleModifier(side_angular_shift,
     203             :                 side_angular_range,
     204             :                 azi_tol,
     205             :                 _start_angle,
     206             :                 original_start_down,
     207             :                 original_start_down_it,
     208             :                 original_start_up,
     209             :                 original_start_up_it,
     210             :                 azi_to_keep_start,
     211             :                 azi_to_mod_start);
     212             : 
     213             :   Real azi_to_mod_end;
     214             :   Real azi_to_keep_end;
     215             : 
     216             :   // For the two mesh azimuthal angles identified, determine which need to be moved to the ending
     217             :   // edge position.
     218          39 :   angleModifier(side_angular_shift,
     219             :                 side_angular_range,
     220             :                 azi_tol,
     221             :                 _end_angle,
     222             :                 original_end_down,
     223             :                 original_end_down_it,
     224             :                 original_end_up,
     225             :                 original_end_up_it,
     226             :                 azi_to_keep_end,
     227             :                 azi_to_mod_end);
     228             : 
     229          39 :   if (azi_to_mod_end == azi_to_mod_start)
     230           2 :     paramError("angle_range",
     231             :                "The azimuthal intervals of the input mesh are too coarse for this parameter.");
     232             : 
     233          72 :   const auto & side_list = mesh.get_boundary_info().build_side_list();
     234             : 
     235             :   // See if the block that contains the external boundary is modified or not
     236          37 :   bool external_block_change(false);
     237        3061 :   for (unsigned int i = 0; i < side_list.size(); i++)
     238             :   {
     239        3024 :     if (std::get<2>(side_list[i]) == OUTER_SIDESET_ID)
     240             :     {
     241        1008 :       dof_id_type block_id_tmp = mesh.elem_ref(std::get<0>(side_list[i])).subdomain_id();
     242        1008 :       if (std::find(_old_block_ids.begin(), _old_block_ids.end(), block_id_tmp) !=
     243             :           _old_block_ids.end())
     244         196 :         external_block_change = true;
     245             :     }
     246             :   }
     247             : 
     248          74 :   mesh.get_boundary_info().build_node_list_from_side_list();
     249             :   std::vector<std::tuple<dof_id_type, boundary_id_type>> node_list =
     250          72 :       mesh.get_boundary_info().build_node_list();
     251             : 
     252          35 :   std::vector<std::pair<Real, dof_id_type>> node_id_keep_start;
     253          35 :   std::vector<std::pair<Real, dof_id_type>> node_id_mod_start;
     254          35 :   std::vector<std::pair<Real, dof_id_type>> node_id_keep_end;
     255          35 :   std::vector<std::pair<Real, dof_id_type>> node_id_mod_end;
     256             : 
     257             :   // Determine which nodes are involved in the elements that are intercepted by the starting/ending
     258             :   // angles
     259             :   Real max_quad_elem_rad(0.0);
     260          37 :   if (mesh.elem_ref(0).n_vertices() == 4)
     261          63 :     for (dof_id_type i = 0;
     262          70 :          i < (_num_sectors_per_side_meta[0] / 2 + 1) * (_num_sectors_per_side_meta[0] / 2 + 1);
     263             :          i++)
     264             :       max_quad_elem_rad =
     265          63 :           max_quad_elem_rad < mesh.node_ref(i).norm() ? mesh.node_ref(i).norm() : max_quad_elem_rad;
     266       13065 :   for (const auto & node_ptr : as_range(mesh.nodes_begin(), mesh.nodes_end()))
     267             :   {
     268        6477 :     const Node & node = *node_ptr;
     269        6477 :     Real node_azi = atan2(node(1), node(0)) * 180.0 / M_PI;
     270        6477 :     Real node_rad = std::sqrt(node(0) * node(0) + node(1) * node(1));
     271        6477 :     if (node_rad > max_quad_elem_rad + rad_tol &&
     272        6188 :         (std::abs(node_azi - azi_to_mod_start) < azi_tol ||
     273        6012 :          std::abs(std::abs(node_azi - azi_to_mod_start) - 360.0) < azi_tol))
     274         176 :       node_id_mod_start.push_back(std::make_pair(node_rad, node.id()));
     275        6477 :     if (node_rad > max_quad_elem_rad + rad_tol &&
     276        6188 :         (std::abs(node_azi - azi_to_keep_start) < azi_tol ||
     277        6012 :          std::abs(std::abs(node_azi - azi_to_keep_start) - 360.0) < azi_tol))
     278         176 :       node_id_keep_start.push_back(std::make_pair(node_rad, node.id()));
     279        6477 :     if (node_rad > max_quad_elem_rad + rad_tol &&
     280        6188 :         (std::abs(node_azi - azi_to_mod_end) < azi_tol ||
     281        6012 :          std::abs(std::abs(node_azi - azi_to_mod_end) - 360.0) < azi_tol))
     282         176 :       node_id_mod_end.push_back(std::make_pair(node_rad, node.id()));
     283        6477 :     if (node_rad > max_quad_elem_rad + rad_tol &&
     284        6188 :         (std::abs(node_azi - azi_to_keep_end) < azi_tol ||
     285        6012 :          std::abs(std::abs(node_azi - azi_to_keep_end) - 360.0) < azi_tol))
     286         176 :       node_id_keep_end.push_back(std::make_pair(node_rad, node.id()));
     287          37 :   }
     288             :   // Sort the involved nodes using radius as the key; this facilitates the determination of circular
     289             :   // regions nodes
     290          37 :   std::sort(node_id_mod_start.begin(), node_id_mod_start.end());
     291          37 :   std::sort(node_id_keep_start.begin(), node_id_keep_start.end());
     292          37 :   std::sort(node_id_mod_end.begin(), node_id_mod_end.end());
     293          37 :   std::sort(node_id_keep_end.begin(), node_id_keep_end.end());
     294          35 :   std::vector<Real> circular_rad_list;
     295          35 :   std::vector<Real> non_circular_rad_list;
     296             : 
     297             :   // Modify the nodes with the azimuthal angles identified before.
     298          37 :   nodeModifier(mesh,
     299             :                node_id_mod_start,
     300             :                node_id_keep_start,
     301             :                circular_rad_list,
     302             :                non_circular_rad_list,
     303             :                node_list,
     304             :                _start_angle,
     305             :                external_block_change,
     306             :                rad_tol);
     307          37 :   nodeModifier(mesh,
     308             :                node_id_mod_end,
     309             :                node_id_keep_end,
     310             :                circular_rad_list,
     311             :                non_circular_rad_list,
     312             :                node_list,
     313             :                _end_angle,
     314             :                external_block_change,
     315             :                rad_tol);
     316             : 
     317             :   const Real max_circular_radius =
     318          37 :       *std::max_element(circular_rad_list.begin(), circular_rad_list.end());
     319             :   const Real min_non_circular_radius =
     320          37 :       *std::min_element(non_circular_rad_list.begin(), non_circular_rad_list.end());
     321             : 
     322             :   // Before re-correcting the radii, correct the mid-point of the elements that are altered
     323          37 :   if (order == 2)
     324        1582 :     for (const auto & elem : mesh.element_ptr_range())
     325             :     {
     326         784 :       const Point & original_vertex_avg = vertex_avgs[elem->id()];
     327         784 :       const Point & new_vertex_avg = elem->vertex_average();
     328         784 :       if (MooseUtils::absoluteFuzzyGreaterThan((original_vertex_avg - new_vertex_avg).norm(), 0.0))
     329             :       {
     330         784 :         if (elem->type() == TRI6 || elem->type() == TRI7)
     331             :         {
     332         196 :           *elem->node_ptr(3) = midPointCorrector(
     333         196 :               *elem->node_ptr(0), *elem->node_ptr(1), max_circular_radius, rad_tol);
     334         196 :           *elem->node_ptr(4) = midPointCorrector(
     335         196 :               *elem->node_ptr(1), *elem->node_ptr(2), max_circular_radius, rad_tol);
     336         196 :           *elem->node_ptr(5) = midPointCorrector(
     337         196 :               *elem->node_ptr(2), *elem->node_ptr(0), max_circular_radius, rad_tol);
     338         196 :           if (elem->type() == TRI7)
     339             :             *elem->node_ptr(6) = (*elem->node_ptr(0) + *elem->node_ptr(1) + *elem->node_ptr(2) +
     340           0 :                                   *elem->node_ptr(3) + *elem->node_ptr(4) + *elem->node_ptr(5)) /
     341             :                                  6.0;
     342             :         }
     343         588 :         else if (elem->type() == QUAD8 || elem->type() == QUAD9)
     344             :         {
     345         588 :           *elem->node_ptr(4) = midPointCorrector(
     346         588 :               *elem->node_ptr(0), *elem->node_ptr(1), max_circular_radius, rad_tol);
     347         588 :           *elem->node_ptr(5) = midPointCorrector(
     348         588 :               *elem->node_ptr(1), *elem->node_ptr(2), max_circular_radius, rad_tol);
     349         588 :           *elem->node_ptr(6) = midPointCorrector(
     350         588 :               *elem->node_ptr(2), *elem->node_ptr(3), max_circular_radius, rad_tol);
     351         588 :           *elem->node_ptr(7) = midPointCorrector(
     352         588 :               *elem->node_ptr(3), *elem->node_ptr(0), max_circular_radius, rad_tol);
     353         588 :           if (elem->type() == QUAD9)
     354             :             *elem->node_ptr(8) = (*elem->node_ptr(0) + *elem->node_ptr(1) + *elem->node_ptr(2) +
     355             :                                   *elem->node_ptr(3) + *elem->node_ptr(4) + *elem->node_ptr(5) +
     356         588 :                                   *elem->node_ptr(6) + *elem->node_ptr(7)) /
     357             :                                  8.0;
     358             :         }
     359             :       }
     360           7 :     }
     361             : 
     362          35 :   std::vector<Real> new_azimuthal_angle;
     363       13065 :   for (const auto & node_ptr : as_range(mesh.nodes_begin(), mesh.nodes_end()))
     364             :   {
     365        6477 :     const Node & node = *node_ptr;
     366        6477 :     if (MooseUtils::absoluteFuzzyEqual(
     367        6477 :             std::sqrt(node(0) * node(0) + node(1) * node(1)), max_circular_radius, rad_tol))
     368             :     {
     369        1204 :       Real node_azi = atan2(node(1), node(0)) * 180.0 / M_PI;
     370        1204 :       new_azimuthal_angle.push_back(node_azi);
     371             :     }
     372          37 :   }
     373          37 :   std::sort(new_azimuthal_angle.begin(), new_azimuthal_angle.end());
     374          37 :   _azimuthal_angle_meta = new_azimuthal_angle;
     375             :   const bool is_first_value_vertex_mod =
     376          37 :       order == 1 ? true : MooseUtils::absoluteFuzzyEqual(_azimuthal_angle_meta[0], -180.0);
     377             : 
     378             :   Real radiusCorrectionFactor_mod =
     379          37 :       _preserve_volumes ? PolygonalMeshGenerationUtils::radiusCorrectionFactor(
     380          37 :                               _azimuthal_angle_meta, true, order, is_first_value_vertex_mod)
     381             :                         : 1.0;
     382             : 
     383             :   // Re-correct Radii
     384          37 :   if (_preserve_volumes)
     385             :   {
     386          37 :     if (min_non_circular_radius <
     387          37 :         max_circular_radius / radiusCorrectionFactor_original * radiusCorrectionFactor_mod)
     388           2 :       mooseError("In AzimuthalBlockSplitGenerator ",
     389           2 :                  _name,
     390             :                  ": the circular region is overlapped with background region after correction.");
     391       12607 :     for (Node * node_ptr : as_range(mesh.nodes_begin(), mesh.nodes_end()))
     392             :     {
     393             :       Node & node = *node_ptr;
     394        6251 :       Real node_rad = std::sqrt(node(0) * node(0) + node(1) * node(1));
     395             :       // Any nodes with radii smaller than the threshold belong to circular regions
     396        6251 :       if (node_rad > rad_tol && node_rad <= max_circular_radius + rad_tol)
     397             :       {
     398        4676 :         const Real node_azi = atan2(node(1), node(0));
     399        4676 :         const Real node_rad_corr =
     400        4676 :             node_rad / radiusCorrectionFactor_original * radiusCorrectionFactor_mod;
     401        4676 :         node(0) = node_rad_corr * std::cos(node_azi);
     402        4676 :         node(1) = node_rad_corr * std::sin(node_azi);
     403             :       }
     404          35 :     }
     405             :   }
     406             :   // Assign New Block IDs
     407         126 :   for (unsigned int block_id_index = 0; block_id_index < _old_block_ids.size(); block_id_index++)
     408             :     for (auto & elem :
     409         182 :          as_range(mesh.active_subdomain_elements_begin(_old_block_ids[block_id_index]),
     410        5404 :                   mesh.active_subdomain_elements_end(_old_block_ids[block_id_index])))
     411             :     {
     412        2520 :       auto p_cent = elem->true_centroid();
     413        2520 :       auto p_cent_azi = atan2(p_cent(1), p_cent(0)) * 180.0 / M_PI;
     414        2520 :       if (_start_angle < _end_angle && p_cent_azi >= _start_angle && p_cent_azi <= _end_angle)
     415         546 :         elem->subdomain_id() = _new_block_ids[block_id_index];
     416        1974 :       else if (_start_angle > _end_angle &&
     417           0 :                (p_cent_azi >= _start_angle || p_cent_azi <= _end_angle))
     418           0 :         elem->subdomain_id() = _new_block_ids[block_id_index];
     419          91 :     }
     420             : 
     421             :   // Assign new Block Names if applicable
     422         126 :   for (unsigned int i = 0; i < _new_block_names.size(); i++)
     423          91 :     mesh.subdomain_name(_new_block_ids[i]) = _new_block_names[i];
     424             : 
     425          35 :   MeshTools::Modification::rotate(mesh, -90.0, 0.0, 0.0);
     426             : 
     427             :   // Workaround - the point locator clear should be done in rotate()
     428          35 :   mesh.clear_point_locator();
     429             : 
     430             :   // The rotate() could unset this (because spatial_dimension() can be
     431             :   // changed) but let's unset to be sure our subdomain changes get
     432             :   // recached.
     433             :   mesh.unset_has_cached_elem_data();
     434             : 
     435        1183 :   for (unsigned int i = 0; i < _azimuthal_angle_meta.size(); i++)
     436        1148 :     _azimuthal_angle_meta[i] = (_azimuthal_angle_meta[i] - 90.0 <= -180.0)
     437        1148 :                                    ? (_azimuthal_angle_meta[i] + 270.0)
     438             :                                    : _azimuthal_angle_meta[i] - 90.0;
     439          35 :   std::sort(_azimuthal_angle_meta.begin(), _azimuthal_angle_meta.end());
     440             : 
     441          70 :   return std::move(_input);
     442             : }
     443             : 
     444             : void
     445          78 : AzimuthalBlockSplitGenerator::angleIdentifier(const Real & terminal_angle,
     446             :                                               Real & original_down,
     447             :                                               std::vector<Real>::iterator & original_down_it,
     448             :                                               Real & original_up,
     449             :                                               std::vector<Real>::iterator & original_up_it,
     450             :                                               std::vector<Real> & azimuthal_angles_vtx)
     451             : {
     452             : 
     453             :   auto term_up =
     454          78 :       std::lower_bound(azimuthal_angles_vtx.begin(), azimuthal_angles_vtx.end(), terminal_angle);
     455          78 :   if (term_up == azimuthal_angles_vtx.begin())
     456             :   {
     457           0 :     original_up = *term_up;
     458           0 :     original_up_it = term_up;
     459           0 :     original_down = -180.0;
     460           0 :     original_down_it = azimuthal_angles_vtx.end() - 1;
     461             :   }
     462          78 :   else if (term_up == azimuthal_angles_vtx.end())
     463             :   {
     464           0 :     original_down = azimuthal_angles_vtx.back();
     465           0 :     original_down_it = azimuthal_angles_vtx.end() - 1;
     466           0 :     original_up = 180.0;
     467           0 :     original_up_it = azimuthal_angles_vtx.begin();
     468             :   }
     469             :   else
     470             :   {
     471          78 :     original_down = *(term_up - 1);
     472          78 :     original_down_it = term_up - 1;
     473          78 :     original_up = *term_up;
     474          78 :     original_up_it = term_up;
     475             :   }
     476          78 : }
     477             : 
     478             : void
     479          78 : AzimuthalBlockSplitGenerator::angleModifier(const Real & side_angular_shift,
     480             :                                             const Real & side_angular_range,
     481             :                                             const Real & azi_tol,
     482             :                                             const Real & terminal_angle,
     483             :                                             const Real & original_down,
     484             :                                             std::vector<Real>::iterator & original_down_it,
     485             :                                             const Real & original_up,
     486             :                                             std::vector<Real>::iterator & original_up_it,
     487             :                                             Real & azi_to_keep,
     488             :                                             Real & azi_to_mod)
     489             : {
     490             :   // Half interval is used to help determine which of the two identified azimuthal angles needs to
     491             :   // be moved.
     492          78 :   const Real half_interval = (original_up - original_down) / 2.0;
     493             :   // In this case, the lower azimuthal angle matches a vertex position, while the target angle is
     494             :   // not overlapped with the same vertex position. Thus the lower azimuthal angle is not moved
     495             :   // anyway.
     496          78 :   if (std::abs((original_down + side_angular_shift) / side_angular_range -
     497          78 :                std::round((original_down + side_angular_shift) / side_angular_range)) < azi_tol &&
     498          35 :       std::abs(original_down - terminal_angle) > azi_tol)
     499             :   {
     500          35 :     azi_to_keep = original_down;
     501          35 :     azi_to_mod = original_up;
     502          35 :     *original_up_it = terminal_angle;
     503             :   }
     504             :   // In this case, the upper azimuthal angle matches a vertex position, while the target angle is
     505             :   // not overlapped with the same vertex position. Thus the upper azimuthal angle is not moved
     506             :   // anyway.
     507          43 :   else if (std::abs((original_up + side_angular_shift) / side_angular_range -
     508          43 :                     std::round((original_up + side_angular_shift) / side_angular_range)) <
     509          43 :                azi_tol &&
     510          41 :            std::abs(original_up - terminal_angle) > azi_tol)
     511             :   {
     512          41 :     azi_to_keep = original_up;
     513          41 :     azi_to_mod = original_down;
     514          41 :     *original_down_it = terminal_angle;
     515             :   }
     516             :   // Move upper azimuthal angle as it is closer to the target angle.
     517           2 :   else if (terminal_angle - original_down > half_interval)
     518             :   {
     519           2 :     azi_to_keep = original_down;
     520           2 :     azi_to_mod = original_up;
     521           2 :     *original_up_it = terminal_angle;
     522             :   }
     523             :   // Move lower azimuthal angle as it is closer to the target angle.
     524             :   else
     525             :   {
     526           0 :     azi_to_keep = original_up;
     527           0 :     azi_to_mod = original_down;
     528           0 :     *original_down_it = terminal_angle;
     529             :   }
     530          78 : }
     531             : 
     532             : void
     533          74 : AzimuthalBlockSplitGenerator::nodeModifier(
     534             :     ReplicatedMesh & mesh,
     535             :     const std::vector<std::pair<Real, dof_id_type>> & node_id_mod,
     536             :     const std::vector<std::pair<Real, dof_id_type>> & node_id_keep,
     537             :     std::vector<Real> & circular_rad_list,
     538             :     std::vector<Real> & non_circular_rad_list,
     539             :     const std::vector<std::tuple<dof_id_type, boundary_id_type>> & node_list,
     540             :     const Real & term_angle,
     541             :     const bool & external_block_change,
     542             :     const Real & rad_tol)
     543             : {
     544         426 :   for (unsigned int i = 0; i < node_id_mod.size(); i++)
     545             :   {
     546             :     // Circular regions, radius is not altered
     547         352 :     if (std::abs(node_id_mod[i].first - node_id_keep[i].first) < rad_tol)
     548             :     {
     549         264 :       Node & node_mod = mesh.node_ref(node_id_mod[i].second);
     550         264 :       circular_rad_list.push_back(node_id_mod[i].first);
     551         264 :       node_mod(0) = node_id_mod[i].first * std::cos(term_angle * M_PI / 180.0);
     552         264 :       node_mod(1) = node_id_mod[i].first * std::sin(term_angle * M_PI / 180.0);
     553             :     }
     554             :     else
     555             :     {
     556          88 :       non_circular_rad_list.push_back(node_id_mod[i].first);
     557             :       // For external boundary nodes, if the external block is not modified, the boundary nodes are
     558             :       // not moved.
     559             :       // Non-circular range, use intercept instead
     560          88 :       if (std::find(node_list.begin(),
     561             :                     node_list.end(),
     562         162 :                     std::make_tuple(node_id_mod[i].second, OUTER_SIDESET_ID)) == node_list.end() ||
     563          74 :           external_block_change)
     564             :       {
     565          28 :         Node & node_mod = mesh.node_ref(node_id_mod[i].second);
     566          28 :         const Node & node_keep = mesh.node_ref(node_id_keep[i].second);
     567          28 :         std::pair<Real, Real> pair_tmp = fourPointIntercept(
     568          28 :             std::make_pair(node_mod(0), node_mod(1)),
     569          28 :             std::make_pair(node_keep(0), node_keep(1)),
     570          56 :             std::make_pair(0.0, 0.0),
     571          56 :             std::make_pair(2.0 * node_id_mod[i].first * std::cos(term_angle * M_PI / 180.0),
     572          28 :                            2.0 * node_id_mod[i].first * std::sin(term_angle * M_PI / 180.0)));
     573          28 :         node_mod(0) = pair_tmp.first;
     574          28 :         node_mod(1) = pair_tmp.second;
     575             :       }
     576             :     }
     577             :   }
     578          74 : }
     579             : 
     580             : Point
     581        2940 : AzimuthalBlockSplitGenerator::midPointCorrector(const Point vertex_0,
     582             :                                                 const Point vertex_1,
     583             :                                                 const Real max_radius,
     584             :                                                 const Real rad_tol)
     585             : {
     586             :   // Check if two vertices have the same radius
     587        2940 :   const Real r_vertex_0 = std::sqrt(vertex_0(0) * vertex_0(0) + vertex_0(1) * vertex_0(1));
     588        2940 :   const Real r_vertex_1 = std::sqrt(vertex_1(0) * vertex_1(0) + vertex_1(1) * vertex_1(1));
     589             :   // If both vertices have the same radius and they are located in the circular region,
     590             :   // their midpoint should have the same radius and has the average azimuthal angle of the two
     591             :   // vertices.
     592        2940 :   if (std::abs(r_vertex_0 - r_vertex_1) < rad_tol && r_vertex_0 < max_radius + rad_tol)
     593        1176 :     return (vertex_0 + vertex_1).unit() * r_vertex_0;
     594             :   else
     595             :     return (vertex_0 + vertex_1) / 2.0;
     596             : }

Generated by: LCOV version 1.14