Skip to content

Commit 8835afe

Browse files
committed
Improve subchannel mesh generator validation
Guard quad and tri assembly mesh generators against invalid axial grids, zero-length domains, and malformed spacer/blockage inputs before mesh construction. Keep quadrilateral assemblies consistent by rejecting only 1x1 layouts while allowing valid 1xN and Nx1 subchannel meshes. Fix triangular assembly block-id fallback handling so the deprecated block_id parameter is still honored when subchannel_block_id is not set. Also add a quad mesh-object guard so pin index lookup on no-pin 1xN/Nx1 meshes reports a clear error instead of evaluating unsigned underflow arithmetic. Add detailed quad coverage for the 1x1 rejection while preserving 1x2 and Nx1 generation behavior. Refs idaholab#32847
1 parent f009851 commit 8835afe

6 files changed

Lines changed: 85 additions & 19 deletions

File tree

modules/subchannel/src/mesh/QuadSubChannelMesh.C

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ QuadSubChannelMesh::channelIndex(const Point & pt) const
9797
unsigned int
9898
QuadSubChannelMesh::getPinIndexFromPoint(const Point & p) const
9999
{
100+
if (_n_pins == 0)
101+
mooseError(name(), ": Cannot compute a pin index because this mesh has no pins.");
102+
100103
Real offset_x = (_nx - 2) * _pitch / 2.0;
101104
Real offset_y = (_ny - 2) * _pitch / 2.0;
102105
unsigned int i = (p(0) + offset_x) / _pitch;
@@ -107,6 +110,9 @@ QuadSubChannelMesh::getPinIndexFromPoint(const Point & p) const
107110
unsigned int
108111
QuadSubChannelMesh::pinIndex(const Point & p) const
109112
{
113+
if (_n_pins == 0)
114+
mooseError(name(), ": Cannot compute a pin index because this mesh has no pins.");
115+
110116
Real offset_x = (_nx - 2) * _pitch / 2.0;
111117
Real offset_y = (_ny - 2) * _pitch / 2.0;
112118
unsigned int i = (p(0) + offset_x) / _pitch;

modules/subchannel/src/meshgenerators/SCMDetailedQuadAssemblyMeshGenerator.C

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,31 @@ SCMDetailedQuadAssemblyMeshGenerator::SCMDetailedQuadAssemblyMeshGenerator(
6363
_n_cells(getParam<unsigned int>("n_cells")),
6464
_nx(getParam<unsigned int>("nx")),
6565
_ny(getParam<unsigned int>("ny")),
66-
_n_channels(_nx * _ny),
66+
_n_channels(0),
6767
_side_gap(isParamValid("side_gap") ? getParam<Real>("side_gap") : getParam<Real>("gap")),
6868
_num_radial_parts(getParam<unsigned int>("num_radial_parts")),
6969
_subchannel_block_id(getParam<unsigned int>("subchannel_block_id")),
7070
_pin_block_id(getParam<unsigned int>("pin_block_id")),
7171
_elem_id(0)
7272
{
7373
const Real L = _unheated_length_entry + _heated_length + _unheated_length_exit;
74+
75+
if (_n_cells == 0)
76+
mooseError(name(), ": The number of axial cells must be greater than zero");
77+
78+
if (L <= 0.0)
79+
mooseError(name(), ": Total bundle length must be greater than zero");
80+
81+
if (_nx == 0 || _ny == 0)
82+
mooseError(name(), ": The number of subchannels must be greater than zero in each direction");
83+
84+
if (_nx < 2 && _ny < 2)
85+
mooseError(name(),
86+
": The number of subchannels cannot be less than 2 in both directions. "
87+
"Smallest assembly allowed is either 2X1 or 1X2.");
88+
89+
_n_channels = _nx * _ny;
90+
7491
const Real dz = L / _n_cells;
7592
for (unsigned int i = 0; i < _n_cells + 1; i++)
7693
_z_grid.push_back(dz * i);

modules/subchannel/src/meshgenerators/SCMDetailedTriAssemblyMeshGenerator.C

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ SCMDetailedTriAssemblyMeshGenerator::SCMDetailedTriAssemblyMeshGenerator(
5555
_n_rings(getParam<unsigned int>("nrings")),
5656
_flat_to_flat(getParam<Real>("flat_to_flat")),
5757
_num_radial_parts(getParam<unsigned int>("num_radial_parts")),
58-
_subchannel_block_id(isParamValid("subchannel_block_id")
58+
_subchannel_block_id(parameters.isParamSetByUser("subchannel_block_id")
5959
? getParam<unsigned int>("subchannel_block_id")
6060
: getParam<unsigned int>("block_id")),
6161
_pin_block_id(getParam<unsigned int>("pin_block_id")),
@@ -65,12 +65,19 @@ SCMDetailedTriAssemblyMeshGenerator::SCMDetailedTriAssemblyMeshGenerator(
6565
_verbose(getParam<bool>("verbose_flag")),
6666
_elem_id(0)
6767
{
68+
const Real L = _unheated_length_entry + _heated_length + _unheated_length_exit;
69+
6870
if (_n_rings < 2)
6971
mooseError(name(),
7072
": 'nrings' must be at least 2. In this mesh generator, the center pin counts as "
7173
"the first ring, so a 7-pin bundle uses nrings = 2.");
7274

73-
Real L = _unheated_length_entry + _heated_length + _unheated_length_exit;
75+
if (_n_cells == 0)
76+
mooseError(name(), ": The number of axial cells must be greater than zero");
77+
78+
if (L <= 0.0)
79+
mooseError(name(), ": Total bundle length must be greater than zero");
80+
7481
Real dz = L / _n_cells;
7582
for (unsigned int i = 0; i < _n_cells + 1; i++)
7683
_z_grid.push_back(dz * i);

modules/subchannel/src/meshgenerators/SCMQuadAssemblyMeshGenerator.C

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,24 +83,46 @@ SCMQuadAssemblyMeshGenerator::SCMQuadAssemblyMeshGenerator(const InputParameters
8383
_n_cells(getParam<unsigned int>("n_cells")),
8484
_nx(getParam<unsigned int>("nx")),
8585
_ny(getParam<unsigned int>("ny")),
86-
_n_channels(_nx * _ny),
87-
_n_gaps((_nx - 1) * _ny + (_ny - 1) * _nx),
88-
_n_pins((_nx - 1) * (_ny - 1)),
86+
_n_channels(0),
87+
_n_gaps(0),
88+
_n_pins(0),
8989
_side_gap(getParam<Real>("side_gap")),
9090
_subchannel_block_id(getParam<unsigned int>("subchannel_block_id")),
9191
_pin_block_id(getParam<unsigned int>("pin_block_id"))
9292
{
93-
if (_spacer_z.size() != _spacer_k.size())
94-
mooseError(name(), ": Size of vector spacer_z should equal size of spacer_k");
93+
const Real total_length = _unheated_length_entry + _heated_length + _unheated_length_exit;
9594

96-
if (_z_blockage.size() != 2)
97-
mooseError(name(), ": Size of vector z_blockage must be 2");
95+
if (_n_cells == 0)
96+
mooseError(name(), ": The number of axial cells must be greater than zero");
97+
98+
if (total_length <= 0.0)
99+
mooseError(name(), ": Total bundle length must be greater than zero");
100+
101+
if (_nx == 0 || _ny == 0)
102+
mooseError(name(), ": The number of subchannels must be greater than zero in each direction");
98103

99104
if (_nx < 2 && _ny < 2)
100105
mooseError(name(),
101106
": The number of subchannels cannot be less than 2 in both directions. "
102107
"Smallest assembly allowed is either 2X1 or 1X2.");
103108

109+
_n_channels = _nx * _ny;
110+
_n_gaps = (_nx - 1) * _ny + (_ny - 1) * _nx;
111+
_n_pins = (_nx - 1) * (_ny - 1);
112+
113+
if (_spacer_z.size() != _spacer_k.size())
114+
mooseError(name(), ": Size of vector spacer_z should equal size of spacer_k");
115+
116+
for (const auto spacer_z : _spacer_z)
117+
if (spacer_z < 0.0 || spacer_z > total_length)
118+
mooseError(name(), ": Spacer locations must be between zero and total bundle length");
119+
120+
if (_z_blockage.size() != 2)
121+
mooseError(name(), ": Size of vector z_blockage must be 2");
122+
123+
if (_z_blockage.front() > _z_blockage.back())
124+
mooseError(name(), ": z_blockage inlet location must not exceed outlet location");
125+
104126
if (!_index_blockage.empty() &&
105127
*std::max_element(_index_blockage.begin(), _index_blockage.end()) > (_n_channels - 1))
106128
mooseError(name(), ": Blocked subchannel index exceeds valid subchannel range");
@@ -122,10 +144,6 @@ SCMQuadAssemblyMeshGenerator::SCMQuadAssemblyMeshGenerator(const InputParameters
122144
SubChannelMesh::generateZGrid(
123145
_unheated_length_entry, _heated_length, _unheated_length_exit, _n_cells, _z_grid);
124146

125-
if (_spacer_z.size() &&
126-
_spacer_z.back() > _unheated_length_entry + _heated_length + _unheated_length_exit)
127-
mooseError(name(), ": Location of spacers must be less than total bundle length");
128-
129147
initializeChannelData();
130148
}
131149

modules/subchannel/src/meshgenerators/SCMTriAssemblyMeshGenerator.C

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ SCMTriAssemblyMeshGenerator::SCMTriAssemblyMeshGenerator(const InputParameters &
6363
_unheated_length_entry(getParam<Real>("unheated_length_entry")),
6464
_heated_length(getParam<Real>("heated_length")),
6565
_unheated_length_exit(getParam<Real>("unheated_length_exit")),
66-
_subchannel_block_id(isParamValid("subchannel_block_id")
66+
_subchannel_block_id(params.isParamSetByUser("subchannel_block_id")
6767
? getParam<unsigned int>("subchannel_block_id")
6868
: getParam<unsigned int>("block_id")),
6969
_pin_block_id(getParam<unsigned int>("pin_block_id")),
@@ -88,21 +88,32 @@ SCMTriAssemblyMeshGenerator::SCMTriAssemblyMeshGenerator(const InputParameters &
8888
_n_gaps(0),
8989
_elem_id(0)
9090
{
91+
const Real total_length = _unheated_length_entry + _heated_length + _unheated_length_exit;
92+
9193
if (_n_rings < 2)
9294
mooseError(name(),
9395
": 'nrings' must be at least 2. In this mesh generator, the center pin counts as "
9496
"the first ring, so a 7-pin bundle uses nrings = 2.");
9597

98+
if (_n_cells == 0)
99+
mooseError(name(), ": The number of axial cells must be greater than zero");
100+
101+
if (total_length <= 0.0)
102+
mooseError(name(), ": Total bundle length must be greater than zero");
103+
96104
if (_spacer_z.size() != _spacer_k.size())
97105
mooseError(name(), ": Size of vector spacer_z should be equal to size of vector spacer_k");
98106

99-
if (_spacer_z.size() &&
100-
_spacer_z.back() > _unheated_length_entry + _heated_length + _unheated_length_exit)
101-
mooseError(name(), ": Location of spacers should be less than the total bundle length");
107+
for (const auto spacer_z : _spacer_z)
108+
if (spacer_z < 0.0 || spacer_z > total_length)
109+
mooseError(name(), ": Location of spacers should be between zero and total bundle length");
102110

103111
if (_z_blockage.size() != 2)
104112
mooseError(name(), ": Size of vector z_blockage must be 2");
105113

114+
if (_z_blockage.front() > _z_blockage.back())
115+
mooseError(name(), ": z_blockage inlet location must not exceed outlet location");
116+
106117
if (*max_element(_reduction_blockage.begin(), _reduction_blockage.end()) > 1)
107118
mooseError(name(), ": The area reduction of the blocked subchannels cannot be more than 1");
108119

@@ -117,7 +128,7 @@ SCMTriAssemblyMeshGenerator::SCMTriAssemblyMeshGenerator(const InputParameters &
117128
_unheated_length_entry, _heated_length, _unheated_length_exit, _n_cells, _z_grid);
118129

119130
// Defining the total length from 3 axial sections
120-
Real L = _unheated_length_entry + _heated_length + _unheated_length_exit;
131+
Real L = total_length;
121132

122133
// Defining the position of the spacer grid in the numerical solution array
123134
std::vector<int> spacer_cell;

modules/subchannel/test/tests/mesh/detailed_quad_assembly/tests

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@
99
recover = false
1010
requirement = 'The system will create a detailed mesh of the subchannels and fuel pins for quadrilateral assemblies (3x3)'
1111
[]
12+
[subchannel_number]
13+
type = 'RunException'
14+
input = 'coords.i'
15+
cli_args = 'Mesh/assembly/nx=1 Mesh/assembly/ny=1'
16+
expect_err = "The number of subchannels cannot be less than 2 in both directions\. Smallest assembly allowed is either 2X1 or 1X2\."
17+
requirement = 'The system shall check the minimum number of subchannels for detailed quadrilateral assemblies'
18+
[]
1219

1320
[1x2]
1421
type = Exodiff

0 commit comments

Comments
 (0)