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 "SCMQuadAssemblyMeshGenerator.h"
11 : #include "SubChannelMesh.h"
12 :
13 : #include "libmesh/edge_edge2.h"
14 :
15 : #include <algorithm>
16 : #include <cmath>
17 : #include <limits>
18 : #include <memory>
19 : #include <numeric>
20 :
21 : registerMooseObject("SubChannelApp", SCMQuadAssemblyMeshGenerator);
22 : registerMooseObjectRenamed("SubChannelApp",
23 : SCMQuadSubChannelMeshGenerator,
24 : "06/30/2027 24:00",
25 : SCMQuadAssemblyMeshGenerator);
26 : registerMooseObjectRenamed("SubChannelApp",
27 : QuadSubChannelMeshGenerator,
28 : "06/30/2027 24:00",
29 : SCMQuadAssemblyMeshGenerator);
30 : registerMooseObjectRenamed("SubChannelApp",
31 : SCMQuadPinMeshGenerator,
32 : "06/30/2027 24:00",
33 : SCMQuadAssemblyMeshGenerator);
34 : registerMooseObjectRenamed("SubChannelApp",
35 : QuadPinMeshGenerator,
36 : "06/30/2027 24:00",
37 : SCMQuadAssemblyMeshGenerator);
38 :
39 : InputParameters
40 428 : SCMQuadAssemblyMeshGenerator::validParams()
41 : {
42 428 : InputParameters params = MeshGenerator::validParams();
43 428 : params.addClassDescription("Creates one mesh containing both 1D subchannels and 1D pins in a "
44 : "square lattice arrangement");
45 :
46 856 : params.addRequiredParam<Real>("pitch", "Pitch [m]");
47 856 : params.addRequiredParam<Real>("pin_diameter", "Rod diameter [m]");
48 856 : params.addParam<Real>("unheated_length_entry", 0.0, "Unheated length at entry [m]");
49 856 : params.addRequiredParam<Real>("heated_length", "Heated length [m]");
50 856 : params.addParam<Real>("unheated_length_exit", 0.0, "Unheated length at exit [m]");
51 :
52 856 : params.addParam<std::vector<Real>>(
53 : "spacer_z", {}, "Axial location of spacers/vanes/mixing vanes [m]");
54 856 : params.addParam<std::vector<Real>>(
55 : "spacer_k", {}, "K-loss coefficient of spacers/vanes/mixing vanes [-]");
56 :
57 856 : params.addParam<std::vector<Real>>("z_blockage",
58 856 : std::vector<Real>({0.0, 0.0}),
59 : "Axial location of blockage (inlet, outlet) [m]");
60 856 : params.addParam<std::vector<unsigned int>>("index_blockage",
61 856 : std::vector<unsigned int>({0}),
62 : "Index of subchannels affected by blockage");
63 856 : params.addParam<std::vector<Real>>("reduction_blockage",
64 856 : std::vector<Real>({1.0}),
65 : "Area reduction of subchannels affected by blockage");
66 856 : params.addParam<std::vector<Real>>(
67 856 : "k_blockage", std::vector<Real>({0.0}), "Form loss coefficient of blocked subchannels");
68 :
69 856 : params.addParam<Real>("Kij", 0.5, "Lateral form loss coefficient [-]");
70 856 : params.addRequiredParam<unsigned int>("n_cells", "The number of cells in the axial direction");
71 856 : params.addRequiredParam<unsigned int>("nx", "Number of channels in the x direction [-]");
72 856 : params.addRequiredParam<unsigned int>("ny", "Number of channels in the y direction [-]");
73 :
74 856 : params.addRequiredParam<Real>(
75 : "side_gap",
76 : "The side gap, not to be confused with the gap between pins; this refers to the gap next "
77 : "to the duct or else the distance between the subchannel centroid and the duct wall. "
78 : "distance(edge pin center, duct wall) = pitch / 2 + side_gap [m]");
79 :
80 856 : params.addParam<unsigned int>("subchannel_block_id", 0, "Subchannel block id");
81 856 : params.addParam<unsigned int>("pin_block_id", 1, "Fuel Pin block id");
82 :
83 428 : return params;
84 0 : }
85 :
86 215 : SCMQuadAssemblyMeshGenerator::SCMQuadAssemblyMeshGenerator(const InputParameters & params)
87 : : MeshGenerator(params),
88 215 : _unheated_length_entry(getParam<Real>("unheated_length_entry")),
89 430 : _heated_length(getParam<Real>("heated_length")),
90 430 : _unheated_length_exit(getParam<Real>("unheated_length_exit")),
91 430 : _spacer_z(getParam<std::vector<Real>>("spacer_z")),
92 430 : _spacer_k(getParam<std::vector<Real>>("spacer_k")),
93 430 : _z_blockage(getParam<std::vector<Real>>("z_blockage")),
94 430 : _index_blockage(getParam<std::vector<unsigned int>>("index_blockage")),
95 430 : _reduction_blockage(getParam<std::vector<Real>>("reduction_blockage")),
96 430 : _k_blockage(getParam<std::vector<Real>>("k_blockage")),
97 430 : _kij(getParam<Real>("Kij")),
98 430 : _pitch(getParam<Real>("pitch")),
99 430 : _pin_diameter(getParam<Real>("pin_diameter")),
100 430 : _n_cells(getParam<unsigned int>("n_cells")),
101 430 : _nx(getParam<unsigned int>("nx")),
102 430 : _ny(getParam<unsigned int>("ny")),
103 215 : _n_channels(0),
104 215 : _n_gaps(0),
105 215 : _n_pins(0),
106 430 : _side_gap(getParam<Real>("side_gap")),
107 430 : _subchannel_block_id(getParam<unsigned int>("subchannel_block_id")),
108 645 : _pin_block_id(getParam<unsigned int>("pin_block_id"))
109 : {
110 215 : const Real total_length = _unheated_length_entry + _heated_length + _unheated_length_exit;
111 :
112 215 : if (_n_cells == 0)
113 0 : paramError("n_cells", "The number of axial cells must be greater than zero");
114 :
115 215 : if (total_length <= 0.0)
116 0 : mooseError("Total bundle length must be greater than zero");
117 :
118 215 : if (_nx == 0 || _ny == 0)
119 0 : mooseError("The number of subchannels must be greater than zero in each direction");
120 :
121 215 : if (_nx < 2 && _ny < 2)
122 2 : mooseError("The number of subchannels cannot be less than 2 in both directions. "
123 : "Smallest assembly allowed is either 2X1 or 1X2.");
124 :
125 213 : _n_channels = _nx * _ny;
126 213 : _n_gaps = (_nx - 1) * _ny + (_ny - 1) * _nx;
127 213 : _n_pins = (_nx - 1) * (_ny - 1);
128 :
129 213 : if (_spacer_z.size() != _spacer_k.size())
130 0 : mooseError("Size of vector spacer_z should equal size of spacer_k");
131 :
132 615 : for (const auto spacer_z : _spacer_z)
133 402 : if (spacer_z < 0.0 || spacer_z > total_length)
134 0 : paramError("spacer_z", "Spacer locations must be between zero and total bundle length");
135 :
136 213 : if (_z_blockage.size() != 2)
137 0 : paramError("z_blockage", "Size of vector z_blockage must be 2");
138 :
139 213 : if (_z_blockage.front() > _z_blockage.back())
140 0 : paramError("z_blockage", "z_blockage inlet location must not exceed outlet location");
141 :
142 213 : if (!_index_blockage.empty() &&
143 213 : *std::max_element(_index_blockage.begin(), _index_blockage.end()) > (_n_channels - 1))
144 0 : paramError("index_blockage", "Blocked subchannel index exceeds valid subchannel range");
145 :
146 213 : if (!_reduction_blockage.empty() &&
147 213 : *std::max_element(_reduction_blockage.begin(), _reduction_blockage.end()) > 1.0)
148 0 : paramError("reduction_blockage", "Area reduction of blocked subchannels cannot exceed 1");
149 :
150 213 : if ((_index_blockage.size() > _n_channels) || (_reduction_blockage.size() > _n_channels) ||
151 : (_k_blockage.size() > _n_channels))
152 0 : mooseError("Sizes of blockage-related vectors cannot exceed total number of subchannels");
153 :
154 213 : if ((_index_blockage.size() != _reduction_blockage.size()) ||
155 426 : (_index_blockage.size() != _k_blockage.size()) ||
156 : (_reduction_blockage.size() != _k_blockage.size()))
157 0 : mooseError("index_blockage, reduction_blockage, and k_blockage must have equal size");
158 :
159 213 : SubChannelMesh::generateZGrid(
160 213 : _unheated_length_entry, _heated_length, _unheated_length_exit, _n_cells, _z_grid);
161 :
162 213 : initializeChannelData();
163 213 : }
164 :
165 : void
166 213 : SCMQuadAssemblyMeshGenerator::initializeChannelData()
167 : {
168 : // Defining the total length from 3 axial sections
169 213 : const Real L = _unheated_length_entry + _heated_length + _unheated_length_exit;
170 :
171 : // Defining the position of the spacer grid in the numerical solution array
172 : std::vector<int> spacer_cell;
173 615 : for (const auto & elem : _spacer_z)
174 402 : spacer_cell.emplace_back(std::round(elem * _n_cells / L));
175 :
176 : // Defining the arrays for axial resistances
177 213 : std::vector<Real> kgrid(_n_cells + 1, 0.0);
178 213 : _k_grid.resize(_n_channels, std::vector<Real>(_n_cells + 1));
179 :
180 : // Summing the spacer resistance to the 1D grid resistance array
181 615 : for (unsigned int index = 0; index < spacer_cell.size(); index++)
182 402 : kgrid[spacer_cell[index]] += _spacer_k[index];
183 :
184 : // Creating the 2D grid resistance array
185 3837 : for (unsigned int i = 0; i < _n_channels; i++)
186 3624 : _k_grid[i] = kgrid;
187 :
188 : // Add blockage resistance to the 2D grid resistance array
189 213 : const Real dz = L / _n_cells;
190 4410 : for (unsigned int i = 0; i < _n_cells + 1; i++)
191 4197 : if ((dz * i >= _z_blockage.front() && dz * i <= _z_blockage.back()))
192 : {
193 : unsigned int index = 0;
194 426 : for (const auto & i_ch : _index_blockage)
195 : {
196 213 : _k_grid[i_ch][i] += _k_blockage[index];
197 213 : index++;
198 : }
199 : }
200 :
201 : // Defining the size of the maps
202 213 : _gap_to_chan_map.resize(_n_gaps);
203 213 : _gap_to_pin_map.resize(_n_gaps);
204 213 : _gapnodes.resize(_n_gaps);
205 213 : _chan_to_gap_map.resize(_n_channels);
206 213 : _chan_to_pin_map.resize(_n_channels);
207 213 : _pin_to_chan_map.resize(_n_pins);
208 213 : _sign_id_crossflow_map.resize(_n_channels);
209 213 : _gij_map.resize(_n_cells + 1);
210 213 : _subchannel_position.resize(_n_channels);
211 :
212 3837 : for (unsigned int i = 0; i < _n_channels; i++)
213 : {
214 3624 : _subchannel_position[i].reserve(3);
215 14496 : for (unsigned int j = 0; j < 3; j++)
216 10872 : _subchannel_position.at(i).push_back(0.0);
217 : }
218 :
219 4410 : for (unsigned int iz = 0; iz < _n_cells + 1; iz++)
220 4197 : _gij_map[iz].reserve(_n_gaps);
221 :
222 : // Defining the signs for positive and negative flows
223 213 : const Real positive_flow = 1.0;
224 213 : const Real negative_flow = -1.0;
225 :
226 : // Defining the subchannel types
227 213 : _subch_type.resize(_n_channels);
228 1005 : for (unsigned int iy = 0; iy < _ny; iy++)
229 4416 : for (unsigned int ix = 0; ix < _nx; ix++)
230 : {
231 3624 : const unsigned int i_ch = _nx * iy + ix;
232 3411 : const bool is_corner = (ix == 0 && iy == 0) || (ix == _nx - 1 && iy == 0) ||
233 6825 : (ix == 0 && iy == _ny - 1) || (ix == _nx - 1 && iy == _ny - 1);
234 3624 : const bool is_edge = (ix == 0 || iy == 0 || ix == _nx - 1 || iy == _ny - 1);
235 :
236 : // Two channels side by side (a 1x2/2x1 grid) only occurs in verification cases; treat both
237 : // as CENTER. The smallest physical assembly is 2x2 subchannels around a single pin.
238 3624 : if (_n_channels == 2)
239 18 : _subch_type[i_ch] = EChannelType::CENTER;
240 3606 : else if (_n_channels == 4)
241 104 : _subch_type[i_ch] = EChannelType::CORNER;
242 3502 : else if (is_corner)
243 712 : _subch_type[i_ch] = EChannelType::CORNER;
244 2790 : else if (is_edge)
245 1594 : _subch_type[i_ch] = EChannelType::EDGE;
246 : else
247 1196 : _subch_type[i_ch] = EChannelType::CENTER;
248 : }
249 :
250 : /**
251 : * Build channel-gap connectivity directly from the rectilinear channel grid.
252 : *
253 : * Each gap separates exactly two neighboring channels. The east-west pass connects channels that
254 : * are adjacent in the x direction, and the north-south pass connects channels that are adjacent
255 : * in the y direction.
256 : * As each gap is created, both the reverse map (_gap_to_chan_map) and the forward channel maps
257 : * (_chan_to_gap_map) are filled, and opposite crossflow signs are assigned to the two channels.
258 : *
259 : * Boundary gaps use a half pin-to-pin spacing plus the duct side gap, while interior gaps use the
260 : * full pin-to-pin gap width.
261 : */
262 213 : unsigned int i_gap = 0;
263 1005 : for (unsigned int iy = 0; iy < _ny; iy++)
264 3624 : for (unsigned int ix = 0; ix < _nx - 1; ix++)
265 : {
266 2832 : const unsigned int i_ch = _nx * iy + ix;
267 2832 : const unsigned int j_ch = _nx * iy + (ix + 1);
268 2832 : _gap_to_chan_map[i_gap] = {i_ch, j_ch};
269 2832 : _chan_to_gap_map[i_ch].push_back(i_gap);
270 2832 : _chan_to_gap_map[j_ch].push_back(i_gap);
271 2832 : _sign_id_crossflow_map[i_ch].push_back(positive_flow);
272 2832 : _sign_id_crossflow_map[j_ch].push_back(negative_flow);
273 :
274 : // Make a gap size map.
275 2832 : if (iy == 0 || iy == _ny - 1)
276 1264 : _gij_map[0].push_back((_pitch - _pin_diameter) / 2.0 + _side_gap);
277 : else
278 1568 : _gij_map[0].push_back(_pitch - _pin_diameter);
279 :
280 2832 : ++i_gap;
281 : }
282 :
283 : // Index the north-south gaps.
284 792 : for (unsigned int iy = 0; iy < _ny - 1; iy++)
285 3355 : for (unsigned int ix = 0; ix < _nx; ix++)
286 : {
287 2776 : const unsigned int i_ch = _nx * iy + ix;
288 2776 : const unsigned int j_ch = _nx * (iy + 1) + ix;
289 2776 : _gap_to_chan_map[i_gap] = {i_ch, j_ch};
290 2776 : _chan_to_gap_map[i_ch].push_back(i_gap);
291 2776 : _chan_to_gap_map[j_ch].push_back(i_gap);
292 2776 : _sign_id_crossflow_map[i_ch].push_back(positive_flow);
293 2776 : _sign_id_crossflow_map[j_ch].push_back(negative_flow);
294 :
295 : // Make a gap size map.
296 2776 : if (ix == 0 || ix == _nx - 1)
297 1155 : _gij_map[0].push_back((_pitch - _pin_diameter) / 2.0 + _side_gap);
298 : else
299 1621 : _gij_map[0].push_back(_pitch - _pin_diameter);
300 :
301 2776 : ++i_gap;
302 : }
303 :
304 4197 : for (unsigned int iz = 1; iz < _n_cells + 1; iz++)
305 3984 : _gij_map[iz] = _gij_map[0];
306 :
307 : // Make pin to channel map.
308 792 : for (unsigned int iy = 0; iy < _ny - 1; iy++)
309 2776 : for (unsigned int ix = 0; ix < _nx - 1; ix++)
310 : {
311 2197 : const unsigned int i_pin = (_nx - 1) * iy + ix;
312 2197 : const unsigned int i_chan_1 = _nx * iy + ix;
313 2197 : const unsigned int i_chan_2 = _nx * (iy + 1) + ix;
314 2197 : const unsigned int i_chan_3 = _nx * (iy + 1) + (ix + 1);
315 2197 : const unsigned int i_chan_4 = _nx * iy + (ix + 1);
316 :
317 2197 : _pin_to_chan_map[i_pin].push_back(i_chan_1);
318 2197 : _pin_to_chan_map[i_pin].push_back(i_chan_2);
319 2197 : _pin_to_chan_map[i_pin].push_back(i_chan_3);
320 2197 : _pin_to_chan_map[i_pin].push_back(i_chan_4);
321 : }
322 :
323 : // Set the subchannel positions so that the center of the assembly is the zero point.
324 1005 : for (unsigned int iy = 0; iy < _ny; iy++)
325 4416 : for (unsigned int ix = 0; ix < _nx; ix++)
326 : {
327 3624 : const unsigned int i_ch = _nx * iy + ix;
328 3624 : const Real offset_x = (_nx - 1) * _pitch / 2.0;
329 3624 : const Real offset_y = (_ny - 1) * _pitch / 2.0;
330 3624 : _subchannel_position[i_ch][0] = _pitch * ix - offset_x;
331 3624 : _subchannel_position[i_ch][1] = _pitch * iy - offset_y;
332 : }
333 :
334 213 : if (_n_pins > 0)
335 : {
336 : // Make channel to pin map.
337 984 : for (unsigned int iy = 0; iy < _ny; iy++) // row
338 4386 : for (unsigned int ix = 0; ix < _nx; ix++)
339 : {
340 3606 : const unsigned int i_ch = _nx * iy + ix;
341 :
342 : // Corners contact 1/4 of one pin.
343 3606 : if (iy == 0 && ix == 0)
344 204 : _chan_to_pin_map[i_ch].push_back((_nx - 1) * iy + ix);
345 3402 : else if (iy == _ny - 1 && ix == 0)
346 204 : _chan_to_pin_map[i_ch].push_back((_nx - 1) * (iy - 1) + ix);
347 3198 : else if (iy == 0 && ix == _nx - 1)
348 204 : _chan_to_pin_map[i_ch].push_back((_nx - 1) * iy + ix - 1);
349 2994 : else if (iy == _ny - 1 && ix == _nx - 1)
350 204 : _chan_to_pin_map[i_ch].push_back((_nx - 1) * (iy - 1) + ix - 1);
351 : // Sides contact 1/4 of two pins.
352 2790 : else if (iy == 0)
353 : {
354 425 : _chan_to_pin_map[i_ch].push_back((_nx - 1) * iy + ix);
355 425 : _chan_to_pin_map[i_ch].push_back((_nx - 1) * iy + ix - 1);
356 : }
357 2365 : else if (iy == _ny - 1)
358 : {
359 425 : _chan_to_pin_map[i_ch].push_back((_nx - 1) * (iy - 1) + ix);
360 425 : _chan_to_pin_map[i_ch].push_back((_nx - 1) * (iy - 1) + ix - 1);
361 : }
362 1940 : else if (ix == 0)
363 : {
364 372 : _chan_to_pin_map[i_ch].push_back((_nx - 1) * iy + ix);
365 372 : _chan_to_pin_map[i_ch].push_back((_nx - 1) * (iy - 1) + ix);
366 : }
367 1568 : else if (ix == _nx - 1)
368 : {
369 372 : _chan_to_pin_map[i_ch].push_back((_nx - 1) * iy + ix - 1);
370 372 : _chan_to_pin_map[i_ch].push_back((_nx - 1) * (iy - 1) + ix - 1);
371 : }
372 : // Interior channels contact 1/4 of four pins.
373 : else
374 : {
375 1196 : _chan_to_pin_map[i_ch].push_back((_nx - 1) * iy + ix);
376 1196 : _chan_to_pin_map[i_ch].push_back((_nx - 1) * iy + ix - 1);
377 1196 : _chan_to_pin_map[i_ch].push_back((_nx - 1) * (iy - 1) + ix);
378 1196 : _chan_to_pin_map[i_ch].push_back((_nx - 1) * (iy - 1) + ix - 1);
379 : }
380 : }
381 :
382 : // Make gap to pin map.
383 5803 : for (unsigned int ig = 0; ig < _n_gaps; ig++)
384 : {
385 5599 : const auto i_ch = _gap_to_chan_map[ig].first;
386 5599 : const auto j_ch = _gap_to_chan_map[ig].second;
387 5599 : const auto & i_pins = _chan_to_pin_map[i_ch];
388 5599 : const auto & j_pins = _chan_to_pin_map[j_ch];
389 :
390 : // Initialize with default values.
391 : _gap_to_pin_map[ig] = {10000, 10000};
392 :
393 20765 : for (unsigned int i : i_pins)
394 59918 : for (unsigned int j : j_pins)
395 44752 : if (i == j)
396 : {
397 8788 : if (_gap_to_pin_map[ig].first == 10000)
398 : {
399 5599 : _gap_to_pin_map[ig].first = i;
400 5599 : _gap_to_pin_map[ig].second = i;
401 : }
402 : else
403 3189 : _gap_to_pin_map[ig].second = i;
404 : }
405 : }
406 : }
407 :
408 : // Reduce reserved memory in the channel-to-gap map.
409 3837 : for (auto & gap : _chan_to_gap_map)
410 : gap.shrink_to_fit();
411 :
412 : // Reduce reserved memory in the channel-to-pin map.
413 3837 : for (auto & pin : _chan_to_pin_map)
414 : pin.shrink_to_fit();
415 :
416 : // Reduce reserved memory in the pin-to-channel map.
417 2410 : for (auto & pin : _pin_to_chan_map)
418 : pin.shrink_to_fit();
419 213 : }
420 :
421 : void
422 213 : SCMQuadAssemblyMeshGenerator::buildSubchannelMesh(MeshBase & mesh_base,
423 : BoundaryInfo & boundary_info)
424 : {
425 213 : mesh_base.reserve_elem(mesh_base.n_elem() + _n_cells * _ny * _nx);
426 213 : mesh_base.reserve_nodes(mesh_base.n_nodes() + (_n_cells + 1) * _ny * _nx);
427 :
428 213 : _nodes.resize(_nx * _ny);
429 :
430 213 : const Real offset_x = (_nx - 1) * _pitch / 2.0;
431 213 : const Real offset_y = (_ny - 1) * _pitch / 2.0;
432 :
433 : // Add the points in the shape of a rectilinear grid. The grid is regular on the xy-plane with a
434 : // spacing of `pitch` between points. The grid along z is irregular to account for pin spacers.
435 : // Store pointers in the _nodes array so we can keep track of which points are in which channels.
436 213 : dof_id_type node_id = mesh_base.n_nodes();
437 1005 : for (unsigned int iy = 0; iy < _ny; iy++)
438 4416 : for (unsigned int ix = 0; ix < _nx; ix++)
439 : {
440 3624 : const unsigned int i_ch = _nx * iy + ix;
441 3624 : _nodes[i_ch].reserve(_n_cells + 1);
442 :
443 70077 : for (unsigned int iz = 0; iz < _n_cells + 1; iz++)
444 66453 : _nodes[i_ch].push_back(mesh_base.add_point(
445 132906 : Point(_pitch * ix - offset_x, _pitch * iy - offset_y, _z_grid[iz]), node_id++));
446 : }
447 :
448 : // Add the elements which in this case are 2-node edges that link each subchannel's nodes
449 : // vertically.
450 213 : dof_id_type elem_id = mesh_base.n_elem();
451 1005 : for (unsigned int iy = 0; iy < _ny; iy++)
452 4416 : for (unsigned int ix = 0; ix < _nx; ix++)
453 66453 : for (unsigned int iz = 0; iz < _n_cells; iz++)
454 : {
455 62829 : Elem * elem = mesh_base.add_elem(std::make_unique<Edge2>());
456 62829 : elem->subdomain_id() = _subchannel_block_id;
457 62829 : elem->set_id(elem_id++);
458 :
459 62829 : const unsigned int i_ch = _nx * iy + ix;
460 62829 : elem->set_node(0, _nodes[i_ch][iz]);
461 62829 : elem->set_node(1, _nodes[i_ch][iz + 1]);
462 :
463 62829 : if (iz == 0)
464 3624 : boundary_info.add_side(elem, 0, 0);
465 62829 : if (iz == _n_cells - 1)
466 3624 : boundary_info.add_side(elem, 1, 1);
467 : }
468 :
469 213 : mesh_base.subdomain_name(_subchannel_block_id) = "subchannel";
470 213 : }
471 :
472 : void
473 204 : SCMQuadAssemblyMeshGenerator::buildPinMesh(MeshBase & mesh_base)
474 : {
475 204 : mesh_base.reserve_elem(mesh_base.n_elem() + _n_cells * (_ny - 1) * (_nx - 1));
476 204 : mesh_base.reserve_nodes(mesh_base.n_nodes() + (_n_cells + 1) * (_ny - 1) * (_nx - 1));
477 :
478 204 : _pin_nodes.resize((_nx - 1) * (_ny - 1));
479 :
480 : // Add the points in the shape of a rectilinear grid. The grid is regular on the xy-plane with a
481 : // spacing of `pitch` between points. The grid along z is also regular. Store pointers in the
482 : // _pin_nodes array so we can keep track of which points are in which pins.
483 204 : const Real offset_x = (_nx - 2) * _pitch / 2.0;
484 204 : const Real offset_y = (_ny - 2) * _pitch / 2.0;
485 :
486 204 : dof_id_type node_id = mesh_base.n_nodes();
487 780 : for (unsigned int iy = 0; iy < _ny - 1; iy++)
488 2773 : for (unsigned int ix = 0; ix < _nx - 1; ix++)
489 : {
490 2197 : const unsigned int i_pin = (_nx - 1) * iy + ix;
491 2197 : _pin_nodes[i_pin].reserve(_n_cells + 1);
492 :
493 42135 : for (unsigned int iz = 0; iz < _n_cells + 1; iz++)
494 39938 : _pin_nodes[i_pin].push_back(mesh_base.add_point(
495 79876 : Point(_pitch * ix - offset_x, _pitch * iy - offset_y, _z_grid[iz]), node_id++));
496 : }
497 :
498 : // Add the elements which in this case are 2-node edges that link each pin's nodes vertically.
499 204 : dof_id_type elem_id = mesh_base.n_elem();
500 780 : for (unsigned int iy = 0; iy < _ny - 1; iy++)
501 2773 : for (unsigned int ix = 0; ix < _nx - 1; ix++)
502 39938 : for (unsigned int iz = 0; iz < _n_cells; iz++)
503 : {
504 37741 : Elem * elem = mesh_base.add_elem(std::make_unique<Edge2>());
505 37741 : elem->subdomain_id() = _pin_block_id;
506 37741 : elem->set_id(elem_id++);
507 :
508 37741 : const unsigned int i_pin = (_nx - 1) * iy + ix;
509 37741 : elem->set_node(0, _pin_nodes[i_pin][iz]);
510 37741 : elem->set_node(1, _pin_nodes[i_pin][iz + 1]);
511 : }
512 :
513 204 : mesh_base.subdomain_name(_pin_block_id) = "fuel_pins";
514 204 : }
515 :
516 : void
517 213 : SCMQuadAssemblyMeshGenerator::transferMetadata(QuadSubChannelMesh & sch_mesh)
518 : {
519 : // Move the metadata into QuadSubChannelMesh.
520 213 : sch_mesh._unheated_length_entry = _unheated_length_entry;
521 213 : sch_mesh._heated_length = _heated_length;
522 213 : sch_mesh._unheated_length_exit = _unheated_length_exit;
523 213 : sch_mesh._z_grid = _z_grid;
524 213 : sch_mesh._k_grid = _k_grid;
525 213 : sch_mesh._spacer_z = _spacer_z;
526 213 : sch_mesh._spacer_k = _spacer_k;
527 213 : sch_mesh._z_blockage = _z_blockage;
528 213 : sch_mesh._index_blockage = _index_blockage;
529 213 : sch_mesh._reduction_blockage = _reduction_blockage;
530 213 : sch_mesh._kij = _kij;
531 213 : sch_mesh._pitch = _pitch;
532 213 : sch_mesh._pin_diameter = _pin_diameter;
533 213 : sch_mesh._n_cells = _n_cells;
534 :
535 213 : sch_mesh._nx = _nx;
536 213 : sch_mesh._ny = _ny;
537 213 : sch_mesh._n_channels = _n_channels;
538 213 : sch_mesh._n_gaps = _n_gaps;
539 213 : sch_mesh._n_pins = _n_pins;
540 213 : sch_mesh._side_gap = _side_gap;
541 :
542 213 : sch_mesh._nodes = _nodes;
543 213 : sch_mesh._pin_nodes = _pin_nodes;
544 213 : sch_mesh._gapnodes = _gapnodes;
545 213 : sch_mesh._gap_to_chan_map = _gap_to_chan_map;
546 213 : sch_mesh._gap_to_pin_map = _gap_to_pin_map;
547 213 : sch_mesh._chan_to_gap_map = _chan_to_gap_map;
548 213 : sch_mesh._chan_to_pin_map = _chan_to_pin_map;
549 213 : sch_mesh._pin_to_chan_map = _pin_to_chan_map;
550 213 : sch_mesh._sign_id_crossflow_map = _sign_id_crossflow_map;
551 213 : sch_mesh._gij_map = _gij_map;
552 213 : sch_mesh._subchannel_position = _subchannel_position;
553 213 : sch_mesh._subch_type = _subch_type;
554 :
555 213 : sch_mesh._duct_mesh_exist = false;
556 213 : sch_mesh._pin_mesh_exist = (_n_pins > 0);
557 213 : }
558 :
559 : std::unique_ptr<MeshBase>
560 213 : SCMQuadAssemblyMeshGenerator::generate()
561 : {
562 213 : auto mesh_base = buildMeshBaseObject();
563 : BoundaryInfo & boundary_info = mesh_base->get_boundary_info();
564 :
565 213 : mesh_base->set_spatial_dimension(3);
566 :
567 213 : buildSubchannelMesh(*mesh_base, boundary_info);
568 :
569 213 : if (_n_pins > 0)
570 204 : buildPinMesh(*mesh_base);
571 :
572 213 : boundary_info.sideset_name(0) = "inlet";
573 213 : boundary_info.sideset_name(1) = "outlet";
574 213 : boundary_info.nodeset_name(0) = "inlet";
575 213 : boundary_info.nodeset_name(1) = "outlet";
576 :
577 213 : mesh_base->prepare_for_use();
578 :
579 213 : auto & sch_mesh = static_cast<QuadSubChannelMesh &>(*_mesh);
580 213 : transferMetadata(sch_mesh);
581 213 : sch_mesh.computeAssemblyHydraulicParameters();
582 :
583 213 : return mesh_base;
584 0 : }
|