LCOV - code coverage report
Current view: top level - src/meshgenerators - SCMQuadAssemblyMeshGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose subchannel: #33390 (250e9c) with base 846a5c Lines: 320 333 96.1 %
Date: 2026-07-31 18:21:22 Functions: 7 7 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 "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 : }

Generated by: LCOV version 1.14