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 130 : HexagonalGridPositions::validParams()
17 : {
18 130 : InputParameters params = Positions::validParams();
19 130 : 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 260 : params.addRequiredParam<Point>("center", "Center of the hexagonal grid");
25 260 : 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 260 : params.addRequiredRangeCheckedParam<Real>("pin_pitch", "pin_pitch>0", "Distance between pins");
31 260 : params.addRequiredRangeCheckedParam<unsigned int>("nr", "nr>0", "Number of hexagonal rings");
32 260 : params.addParam<Real>("rotation_around_axis",
33 260 : 0.,
34 : "Rotation angle to apply to the underlying hexagonal lattice (in degrees)");
35 :
36 260 : 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 260 : params.addParam<std::vector<unsigned int>>(
43 : "include_in_pattern", {}, "A vector of the numbers in the pattern to include");
44 260 : 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 260 : 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 130 : params.set<bool>("auto_sort") = false;
58 : // All functors defined on all processes for now
59 130 : params.set<bool>("auto_broadcast") = false;
60 :
61 130 : return params;
62 0 : }
63 :
64 67 : HexagonalGridPositions::HexagonalGridPositions(const InputParameters & parameters)
65 : : Positions(parameters),
66 67 : _center(getParam<Point>("center")),
67 134 : _lattice_flat_to_flat(getParam<Real>("lattice_flat_to_flat")),
68 134 : _pin_pitch(getParam<Real>("pin_pitch")),
69 134 : _z_axis_index(MooseEnum("X Y Z", "Z")),
70 134 : _nr(getParam<unsigned int>("nr")),
71 134 : _pattern(getParam<std::vector<std::vector<unsigned int>>>("pattern")),
72 67 : _include_in_pattern(
73 134 : std::set<unsigned int>(getParam<std::vector<unsigned int>>("include_in_pattern").begin(),
74 67 : getParam<std::vector<unsigned int>>("include_in_pattern").end())),
75 134 : _positions_pattern(getParam<std::vector<std::vector<int>>>("positions_pattern")),
76 134 : _positions_pattern_indexing(
77 67 : getParam<std::map<std::string, unsigned int>>("positions_pattern_indexing"))
78 : {
79 : // Check dimensions
80 67 : 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 65 : 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 63 : if ((_include_in_pattern.empty() && _pattern.size()) ||
97 36 : (_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 108 : for (const auto include : _include_in_pattern)
103 : {
104 : bool found = false;
105 216 : for (const auto & row : _pattern)
106 171 : if (std::find(row.begin(), row.end(), include) != row.end())
107 : found = true;
108 45 : 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 63 : if ((_positions_pattern.empty() && !_positions_pattern_indexing.empty()) ||
116 9 : (!_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 81 : for (const auto & [pos_name, index] : _positions_pattern_indexing)
121 : {
122 : bool found = false;
123 72 : for (const auto & row : _positions_pattern)
124 54 : if (std::find(row.begin(), row.end(), index) != row.end())
125 : found = true;
126 18 : 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 63 : if (((_positions_pattern.size() && _positions_pattern[0].size()) ||
134 72 : _positions_pattern_indexing.size()) &&
135 9 : ((_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 63 : initialize();
142 : // Sort if needed (user-specified)
143 63 : finalize();
144 63 : }
145 :
146 : void
147 63 : HexagonalGridPositions::initialize()
148 : {
149 63 : clearPositions();
150 :
151 : // We make very large pins so they cover the entire position
152 126 : _hex_latt = std::make_unique<HexagonalLatticeUtils>(_lattice_flat_to_flat,
153 : _pin_pitch,
154 63 : _pin_pitch,
155 126 : 0.,
156 63 : 1.,
157 63 : _nr,
158 63 : _z_axis_index,
159 : getParam<Real>("rotation_around_axis"));
160 :
161 126 : if (!isParamSetByUser("positions_pattern"))
162 : {
163 : // Unroll pattern
164 : std::vector<int> pattern_unrolled;
165 :
166 54 : if (_pattern.size())
167 : {
168 : // Check number of pins in pattern
169 36 : std::size_t pattern_size = 0;
170 162 : for (const auto & row : _pattern)
171 126 : pattern_size += row.size();
172 36 : 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 36 : 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 36 : pattern_unrolled.resize(_hex_latt->totalPins(_nr));
185 : unsigned int i = 0;
186 117 : for (const auto r_i : make_range(_nr))
187 441 : 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 360 : _hex_latt->get2DInputPatternIndex(i, row_i, within_row_i);
192 360 : pattern_unrolled[i++] = _pattern[row_i][within_row_i];
193 : }
194 : }
195 : // just needs to be the right size
196 : else
197 18 : 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 54 : if (_include_in_pattern.size())
202 396 : for (const auto patt : pattern_unrolled)
203 360 : if (_include_in_pattern.count(patt) == 0)
204 81 : n_exclusions++;
205 :
206 : // Size array, remove the '-1' / not included positions
207 54 : const auto n_positions = cast_int<std::size_t>(_hex_latt->totalPins(_nr) - n_exclusions);
208 54 : _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 540 : for (const auto patt_i : index_range(pattern_unrolled))
214 486 : if (!_pattern.size() || !_include_in_pattern.size() ||
215 765 : _include_in_pattern.count(pattern_unrolled[patt_i]))
216 405 : _positions[pos_i++] = _hex_latt->pinCenters()[patt_i];
217 54 : }
218 : else
219 : {
220 : // Unroll pattern into positions array
221 9 : const bool initial = _fe_problem.getCurrentExecuteOnFlag() == EXEC_INITIAL;
222 :
223 : // Check number of positions in pattern of nested positions
224 9 : unsigned pattern_size = 0;
225 36 : for (const auto & row : _positions_pattern)
226 27 : pattern_size += row.size();
227 9 : 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 9 : 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 27 : for (const auto & [pos_name, index] : _positions_pattern_indexing)
242 18 : if (_fe_problem.hasUserObject(pos_name))
243 18 : num_pos += _fe_problem.getPositionsObject(pos_name).getNumPositions(initial);
244 9 : _positions.reserve(num_pos);
245 :
246 : // Invert map from positions to indices
247 : std::map<unsigned int, PositionsName> index_to_pos;
248 27 : for (const auto & [pos_name, index] : _positions_pattern_indexing)
249 36 : 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 9 : pattern_unrolled.resize(_hex_latt->totalPins(_nr));
254 : unsigned int i = 0;
255 27 : for (const auto r_i : make_range(_nr))
256 81 : 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 63 : _hex_latt->get2DInputPatternIndex(i, row_i, within_row_i);
261 63 : const auto pos_index = _positions_pattern[row_i][within_row_i];
262 63 : if (auto it = index_to_pos.find(cast_int<unsigned int>(pos_index));
263 : it != index_to_pos.end())
264 54 : pattern_unrolled[i++] = it->second;
265 9 : 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 18 : pattern_unrolled[i++] = "-1";
271 : }
272 :
273 : // Now place the positions in the _positions array with the offset from the parent lattice index
274 72 : for (const auto patt_i : index_range(pattern_unrolled))
275 : {
276 : const auto & pos_name = pattern_unrolled[patt_i];
277 63 : if (pos_name != "-1")
278 396 : for (const auto & pos : _fe_problem.getPositionsObject(pos_name).getPositions(initial))
279 342 : _positions.push_back(_hex_latt->pinCenters()[patt_i] + pos);
280 : }
281 9 : }
282 :
283 63 : _initialized = true;
284 63 : }
|