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 : }
|