LCOV - code coverage report
Current view: top level - src/positions - HexagonalGridPositions.C (source / functions) Hit Total Coverage
Test: idaholab/moose reactor: #31405 (292dce) with base fef103 Lines: 112 130 86.2 %
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 "HexagonalGridPositions.h"
      11             : #include "HexagonalLatticeUtils.h"
      12             : 
      13             : registerMooseObject("ReactorApp", HexagonalGridPositions);
      14             : 
      15             : InputParameters
      16         270 : HexagonalGridPositions::validParams()
      17             : {
      18         270 :   InputParameters params = Positions::validParams();
      19         270 :   params.addClassDescription(
      20             :       "Create positions along a hexagonal grid. Numbering of positions increases first "
      21             :       "counterclockwise, then expanding outwards from the inner ring. "
      22             :       "Inner-numbering is within a radial ring.");
      23             : 
      24         540 :   params.addRequiredParam<Point>("center", "Center of the hexagonal grid");
      25         540 :   params.addRequiredRangeCheckedParam<Real>(
      26             :       "lattice_flat_to_flat",
      27             :       "lattice_flat_to_flat>0",
      28             :       "Distance between two (inner) opposite sides of a lattice. Also known as bundle pitch or "
      29             :       "inner flat-to-flat distance");
      30         540 :   params.addRequiredRangeCheckedParam<Real>("pin_pitch", "pin_pitch>0", "Distance between pins");
      31         540 :   params.addRequiredRangeCheckedParam<unsigned int>("nr", "nr>0", "Number of hexagonal rings");
      32         540 :   params.addParam<Real>("rotation_around_axis",
      33         540 :                         0.,
      34             :                         "Rotation angle to apply to the underlying hexagonal lattice (in degrees)");
      35             : 
      36         540 :   params.addRangeCheckedParam<std::vector<std::vector<unsigned int>>>(
      37             :       "pattern",
      38             :       {},
      39             :       "pattern>=0",
      40             :       "A double-indexed hexagonal-shaped array starting with the upper-left corner. '-1's are not "
      41             :       "selected as positions.");
      42         540 :   params.addParam<std::vector<unsigned int>>(
      43             :       "include_in_pattern", {}, "A vector of the numbers in the pattern to include");
      44         540 :   params.addParam<std::vector<std::vector<int>>>(
      45             :       "positions_pattern",
      46             :       {},
      47             :       "A double-indexed hexagonal-shaped array with the index of other Positions objects starting "
      48             :       "with the upper-left corner. These positions objects will be distributed within the "
      49             :       "hexagonal grid. The indexing is given in 'positions_pattern_indexing'. Use '-1' to discard "
      50             :       "a position in the lattice.");
      51         540 :   params.addParam<std::map<std::string, unsigned int>>(
      52             :       "positions_pattern_indexing",
      53             :       {},
      54             :       "A map from the name of positions objects to their index in the 'positions_pattern'");
      55             : 
      56             :   // Use user-provided ordering
      57         270 :   params.set<bool>("auto_sort") = false;
      58             :   // All functors defined on all processes for now
      59         270 :   params.set<bool>("auto_broadcast") = false;
      60             : 
      61         270 :   return params;
      62           0 : }
      63             : 
      64         137 : HexagonalGridPositions::HexagonalGridPositions(const InputParameters & parameters)
      65             :   : Positions(parameters),
      66         137 :     _center(getParam<Point>("center")),
      67         274 :     _lattice_flat_to_flat(getParam<Real>("lattice_flat_to_flat")),
      68         274 :     _pin_pitch(getParam<Real>("pin_pitch")),
      69         274 :     _z_axis_index(MooseEnum("X Y Z", "Z")),
      70         274 :     _nr(getParam<unsigned int>("nr")),
      71         274 :     _pattern(getParam<std::vector<std::vector<unsigned int>>>("pattern")),
      72         137 :     _include_in_pattern(
      73         274 :         std::set<unsigned int>(getParam<std::vector<unsigned int>>("include_in_pattern").begin(),
      74         137 :                                getParam<std::vector<unsigned int>>("include_in_pattern").end())),
      75         274 :     _positions_pattern(getParam<std::vector<std::vector<int>>>("positions_pattern")),
      76         274 :     _positions_pattern_indexing(
      77         137 :         getParam<std::map<std::string, unsigned int>>("positions_pattern_indexing"))
      78             : {
      79             :   // Check dimensions
      80         137 :   if (_nr == 1)
      81             :   {
      82           2 :     if (MooseUtils::absoluteFuzzyGreaterThan(_pin_pitch, _lattice_flat_to_flat))
      83           2 :       paramError("lattice_flat_to_flat",
      84             :                  "For one ring, the lattice flat to flat must be at least the pin pitch");
      85             :   }
      86             :   else
      87             :   {
      88         135 :     if (MooseUtils::absoluteFuzzyGreaterThan((3 * _nr - 1) * _pin_pitch / sqrt(3),
      89             :                                              _lattice_flat_to_flat))
      90           2 :       paramError("lattice_flat_to_flat",
      91             :                  "Lattice flat to flat distance is less than the minimum (3 * nr - 1) * pin_pitch "
      92             :                  "/ sqrt(3) given nr rings with a pitch of pin_pitch");
      93             :   }
      94             : 
      95             :   // Check pattern and include_in_pattern
      96         133 :   if ((_include_in_pattern.empty() && _pattern.size()) ||
      97          76 :       (_include_in_pattern.size() && _pattern.empty()))
      98           0 :     paramError(
      99             :         "include_in_pattern",
     100             :         "The 'pattern' parameter and the 'include_in_pattern' must be both specified or both not "
     101             :         "specified by the user.");
     102         228 :   for (const auto include : _include_in_pattern)
     103             :   {
     104             :     bool found = false;
     105         456 :     for (const auto & row : _pattern)
     106         361 :       if (std::find(row.begin(), row.end(), include) != row.end())
     107             :         found = true;
     108          95 :     if (!found)
     109           0 :       paramError("include_in_pattern",
     110           0 :                  "Pattern item '" + std::to_string(include) +
     111             :                      "' to include is not present in the pattern");
     112             :   }
     113             : 
     114             :   // Check positions_pattern and positions_pattern_indexing
     115         133 :   if ((_positions_pattern.empty() && !_positions_pattern_indexing.empty()) ||
     116          19 :       (!_positions_pattern.empty() && _positions_pattern_indexing.empty()))
     117           0 :     paramError("positions_pattern_indexing",
     118             :                "The 'positions_pattern' parameter and the 'positions_pattern_indexing' must be "
     119             :                "both specified or both not specified by the user.");
     120         171 :   for (const auto & [pos_name, index] : _positions_pattern_indexing)
     121             :   {
     122             :     bool found = false;
     123         152 :     for (const auto & row : _positions_pattern)
     124         114 :       if (std::find(row.begin(), row.end(), index) != row.end())
     125             :         found = true;
     126          38 :     if (!found)
     127           0 :       paramError("include_in_pattern",
     128           0 :                  "Pattern item '" + pos_name + "' with index '" + std::to_string(index) +
     129             :                      "' to include is not present in the pattern");
     130             :   }
     131             : 
     132             :   // Check incompatible parameters
     133         133 :   if (((_positions_pattern.size() && _positions_pattern[0].size()) ||
     134         152 :        _positions_pattern_indexing.size()) &&
     135          19 :       ((_pattern.size() && _pattern[0].size()) || _include_in_pattern.size()))
     136           0 :     paramError("positions_pattern",
     137             :                "'pattern'/'include_in_pattern' are not supported in combination with "
     138             :                "'positions_pattern/positions_pattern_indexing'. Only one pattern is supported");
     139             : 
     140             :   // Obtain the positions by unrolling the patterns
     141         133 :   initialize();
     142             :   // Sort if needed (user-specified)
     143         133 :   finalize();
     144         133 : }
     145             : 
     146             : void
     147         133 : HexagonalGridPositions::initialize()
     148             : {
     149         133 :   clearPositions();
     150             : 
     151             :   // We make very large pins so they cover the entire position
     152         266 :   _hex_latt = std::make_unique<HexagonalLatticeUtils>(_lattice_flat_to_flat,
     153             :                                                       _pin_pitch,
     154         133 :                                                       _pin_pitch,
     155         266 :                                                       0.,
     156         133 :                                                       1.,
     157         133 :                                                       _nr,
     158         133 :                                                       _z_axis_index,
     159             :                                                       getParam<Real>("rotation_around_axis"));
     160             : 
     161         266 :   if (!isParamSetByUser("positions_pattern"))
     162             :   {
     163             :     // Unroll pattern
     164             :     std::vector<int> pattern_unrolled;
     165             : 
     166         114 :     if (_pattern.size())
     167             :     {
     168             :       // Check number of pins in pattern
     169          76 :       std::size_t pattern_size = 0;
     170         342 :       for (const auto & row : _pattern)
     171         266 :         pattern_size += row.size();
     172          76 :       if (_pattern.size() != cast_int<std::size_t>(2 * _nr - 1))
     173           0 :         mooseError("Number of rows in pattern (",
     174           0 :                    _pattern.size(),
     175             :                    ") should be equal to twice the number of hexagonal rings minus one");
     176          76 :       if (pattern_size != _hex_latt->totalPins(_nr))
     177           0 :         mooseError("Pattern size (",
     178             :                    pattern_size,
     179             :                    ") does not match the number of pins with ",
     180             :                    _nr,
     181             :                    " rings: ",
     182           0 :                    _hex_latt->totalPins(_nr));
     183             : 
     184          76 :       pattern_unrolled.resize(_hex_latt->totalPins(_nr));
     185             :       unsigned int i = 0;
     186         247 :       for (const auto r_i : make_range(_nr))
     187         931 :         for (const auto a_i : make_range(_hex_latt->pins(r_i + 1)))
     188             :         {
     189             :           libmesh_ignore(a_i);
     190             :           unsigned int row_i, within_row_i;
     191         760 :           _hex_latt->get2DInputPatternIndex(i, row_i, within_row_i);
     192         760 :           pattern_unrolled[i++] = _pattern[row_i][within_row_i];
     193             :         }
     194             :     }
     195             :     // just needs to be the right size
     196             :     else
     197          38 :       pattern_unrolled.resize(_hex_latt->totalPins(_nr), 0);
     198             : 
     199             :     // Count the number of positions we do not need to include
     200             :     unsigned int n_exclusions = 0;
     201         114 :     if (_include_in_pattern.size())
     202         836 :       for (const auto patt : pattern_unrolled)
     203         760 :         if (_include_in_pattern.count(patt) == 0)
     204         171 :           n_exclusions++;
     205             : 
     206             :     // Size array, remove the '-1' / not included positions
     207         114 :     const auto n_positions = cast_int<std::size_t>(_hex_latt->totalPins(_nr) - n_exclusions);
     208         114 :     _positions.resize(n_positions);
     209             : 
     210             :     // Fill the positions by retrieving the pin centers at indices included in the pattern (if
     211             :     // specified)
     212             :     unsigned int pos_i = 0;
     213        1140 :     for (const auto patt_i : index_range(pattern_unrolled))
     214        1026 :       if (!_pattern.size() || !_include_in_pattern.size() ||
     215        1615 :           _include_in_pattern.count(pattern_unrolled[patt_i]))
     216         855 :         _positions[pos_i++] = _hex_latt->pinCenters()[patt_i];
     217         114 :   }
     218             :   else
     219             :   {
     220             :     // Unroll pattern into positions array
     221          19 :     const bool initial = _fe_problem.getCurrentExecuteOnFlag() == EXEC_INITIAL;
     222             : 
     223             :     // Check number of positions in pattern of nested positions
     224          19 :     unsigned pattern_size = 0;
     225          76 :     for (const auto & row : _positions_pattern)
     226          57 :       pattern_size += row.size();
     227          19 :     if (_positions_pattern.size() != cast_int<std::size_t>(2 * _nr - 1))
     228           0 :       mooseError("Number of rows in 'positions_pattern' (",
     229           0 :                  _pattern.size(),
     230             :                  ") should be equal to twice the number of hexagonal rings minus one");
     231          19 :     if (pattern_size != _hex_latt->totalPins(_nr))
     232           0 :       mooseError("Pattern size ",
     233             :                  pattern_size,
     234             :                  " does not match the number of pins with ",
     235             :                  _nr,
     236             :                  " rings: ",
     237           0 :                  _hex_latt->totalPins(_nr));
     238             : 
     239             :     // Check that all the positions names are valid
     240             :     unsigned num_pos = 0;
     241          57 :     for (const auto & [pos_name, index] : _positions_pattern_indexing)
     242          38 :       if (_fe_problem.hasUserObject(pos_name))
     243          38 :         num_pos += _fe_problem.getPositionsObject(pos_name).getNumPositions(initial);
     244          19 :     _positions.reserve(num_pos);
     245             : 
     246             :     // Invert map from positions to indices
     247             :     std::map<unsigned int, PositionsName> index_to_pos;
     248          57 :     for (const auto & [pos_name, index] : _positions_pattern_indexing)
     249          76 :       index_to_pos[index] = pos_name;
     250             : 
     251             :     // Unroll pattern : the positions vector is 1D and should be
     252             :     std::vector<PositionsName> pattern_unrolled;
     253          19 :     pattern_unrolled.resize(_hex_latt->totalPins(_nr));
     254             :     unsigned int i = 0;
     255          57 :     for (const auto r_i : make_range(_nr))
     256         171 :       for (const auto a_i : make_range(_hex_latt->pins(r_i + 1)))
     257             :       {
     258             :         libmesh_ignore(a_i);
     259             :         unsigned int row_i, within_row_i;
     260         133 :         _hex_latt->get2DInputPatternIndex(i, row_i, within_row_i);
     261         133 :         const auto pos_index = _positions_pattern[row_i][within_row_i];
     262         133 :         if (auto it = index_to_pos.find(cast_int<unsigned int>(pos_index));
     263             :             it != index_to_pos.end())
     264         114 :           pattern_unrolled[i++] = it->second;
     265          19 :         else if (pos_index != -1)
     266           0 :           paramError("positions_pattern",
     267           0 :                      "Index '" + std::to_string(pos_index) +
     268             :                          "' in pattern is not found in 'positions_pattern_indexing'");
     269             :         else
     270          38 :           pattern_unrolled[i++] = "-1";
     271             :       }
     272             : 
     273             :     // Now place the positions in the _positions array with the offset from the parent lattice index
     274         152 :     for (const auto patt_i : index_range(pattern_unrolled))
     275             :     {
     276             :       const auto & pos_name = pattern_unrolled[patt_i];
     277         133 :       if (pos_name != "-1")
     278         836 :         for (const auto & pos : _fe_problem.getPositionsObject(pos_name).getPositions(initial))
     279         722 :           _positions.push_back(_hex_latt->pinCenters()[patt_i] + pos);
     280             :     }
     281          19 :   }
     282             : 
     283         133 :   _initialized = true;
     284         133 : }

Generated by: LCOV version 1.14