LCOV - code coverage report
Current view: top level - src/meshgenerators - SCMDetailedQuadAssemblyMeshGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose subchannel: #33187 (5aa0b2) with base d7c4bd Lines: 372 376 98.9 %
Date: 2026-06-30 12:24:57 Functions: 4 4 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 "SCMDetailedQuadAssemblyMeshGenerator.h"
      11             : #include "QuadSubChannelMesh.h"
      12             : 
      13             : #include "libmesh/cell_prism6.h"
      14             : #include "libmesh/unstructured_mesh.h"
      15             : 
      16             : #include <array>
      17             : #include <cmath>
      18             : #include <memory>
      19             : 
      20             : registerMooseObject("SubChannelApp", SCMDetailedQuadAssemblyMeshGenerator);
      21             : registerMooseObjectRenamed("SubChannelApp",
      22             :                            SCMDetailedQuadSubChannelMeshGenerator,
      23             :                            "06/30/2027 24:00",
      24             :                            SCMDetailedQuadAssemblyMeshGenerator);
      25             : registerMooseObjectRenamed("SubChannelApp",
      26             :                            DetailedQuadSubChannelMeshGenerator,
      27             :                            "06/30/2027 24:00",
      28             :                            SCMDetailedQuadAssemblyMeshGenerator);
      29             : registerMooseObjectRenamed("SubChannelApp",
      30             :                            SCMDetailedQuadPinMeshGenerator,
      31             :                            "06/30/2027 24:00",
      32             :                            SCMDetailedQuadAssemblyMeshGenerator);
      33             : registerMooseObjectRenamed("SubChannelApp",
      34             :                            DetailedQuadPinMeshGenerator,
      35             :                            "06/30/2027 24:00",
      36             :                            SCMDetailedQuadAssemblyMeshGenerator);
      37             : 
      38             : InputParameters
      39         144 : SCMDetailedQuadAssemblyMeshGenerator::validParams()
      40             : {
      41         144 :   InputParameters params = MeshGenerator::validParams();
      42         144 :   params.addClassDescription(
      43             :       "Creates a detailed mesh of subchannels and fuel pins in a square lattice arrangement");
      44         288 :   params.addRequiredParam<Real>("pitch", "Pitch [m]");
      45         288 :   params.addRequiredParam<Real>("pin_diameter", "Rod diameter [m]");
      46         288 :   params.addParam<Real>("unheated_length_entry", 0.0, "Unheated length at entry [m]");
      47         288 :   params.addRequiredParam<Real>("heated_length", "Heated length [m]");
      48         288 :   params.addParam<Real>("unheated_length_exit", 0.0, "Unheated length at exit [m]");
      49         288 :   params.addRequiredParam<unsigned int>("n_cells", "The number of cells in the axial direction");
      50         288 :   params.addRequiredParam<unsigned int>("nx", "Number of channels in the x direction [-]");
      51         288 :   params.addRequiredParam<unsigned int>("ny", "Number of channels in the y direction [-]");
      52         288 :   params.addRequiredParam<Real>(
      53             :       "side_gap",
      54             :       "The side gap, not to be confused with the gap between pins, this refers to the gap "
      55             :       "next to the duct or else the distance between the subchannel centroid to the duct wall."
      56             :       "distance(edge pin center, duct wall) = pitch / 2 + side_gap [m]");
      57         432 :   params.addRangeCheckedParam<unsigned int>("num_sectors",
      58         288 :                                             16,
      59             :                                             "num_sectors>=4",
      60             :                                             "Number of azimuthal sectors used to discretize each "
      61             :                                             "circular pin cross section.");
      62         288 :   params.addParam<unsigned int>("subchannel_block_id", 0, "Subchannel block id.");
      63         288 :   params.addParam<unsigned int>("pin_block_id", 1, "Fuel pin block id.");
      64         144 :   return params;
      65           0 : }
      66             : 
      67          73 : SCMDetailedQuadAssemblyMeshGenerator::SCMDetailedQuadAssemblyMeshGenerator(
      68          73 :     const InputParameters & parameters)
      69             :   : MeshGenerator(parameters),
      70          73 :     _unheated_length_entry(getParam<Real>("unheated_length_entry")),
      71         146 :     _heated_length(getParam<Real>("heated_length")),
      72         146 :     _unheated_length_exit(getParam<Real>("unheated_length_exit")),
      73         146 :     _pitch(getParam<Real>("pitch")),
      74         146 :     _pin_diameter(getParam<Real>("pin_diameter")),
      75         146 :     _n_cells(getParam<unsigned int>("n_cells")),
      76         146 :     _nx(getParam<unsigned int>("nx")),
      77         146 :     _ny(getParam<unsigned int>("ny")),
      78          73 :     _n_channels(0),
      79         146 :     _side_gap(getParam<Real>("side_gap")),
      80         146 :     _num_sectors(getParam<unsigned int>("num_sectors")),
      81         146 :     _subchannel_block_id(getParam<unsigned int>("subchannel_block_id")),
      82         146 :     _pin_block_id(getParam<unsigned int>("pin_block_id")),
      83          73 :     _elem_id(0)
      84             : {
      85          73 :   const Real L = _unheated_length_entry + _heated_length + _unheated_length_exit;
      86             : 
      87          73 :   if (_n_cells == 0)
      88           0 :     paramError("n_cells", "The number of axial cells must be greater than zero");
      89             : 
      90          73 :   if (L <= 0.0)
      91           0 :     mooseError("Total bundle length must be greater than zero");
      92             : 
      93          73 :   if (_nx == 0 || _ny == 0)
      94           0 :     mooseError("The number of subchannels must be greater than zero in each direction");
      95             : 
      96          73 :   if (_nx < 2 && _ny < 2)
      97           2 :     mooseError("The number of subchannels cannot be less than 2 in both directions. "
      98             :                "Smallest assembly allowed is either 2X1 or 1X2.");
      99             : 
     100          71 :   _n_channels = _nx * _ny;
     101             : 
     102          71 :   const Real dz = L / _n_cells;
     103        2213 :   for (unsigned int i = 0; i < _n_cells + 1; i++)
     104        2142 :     _z_grid.push_back(dz * i);
     105             : 
     106          71 :   _subchannel_position.resize(_n_channels);
     107        1010 :   for (unsigned int i = 0; i < _n_channels; i++)
     108             :   {
     109         939 :     _subchannel_position[i].reserve(3);
     110        3756 :     for (unsigned int j = 0; j < 3; j++)
     111        2817 :       _subchannel_position.at(i).push_back(0.0);
     112             :   }
     113             : 
     114          71 :   _subch_type.resize(_n_channels);
     115         286 :   for (unsigned int iy = 0; iy < _ny; iy++)
     116        1154 :     for (unsigned int ix = 0; ix < _nx; ix++)
     117             :     {
     118         939 :       const unsigned int i_ch = _nx * iy + ix;
     119         868 :       const bool is_corner = (ix == 0 && iy == 0) || (ix == _nx - 1 && iy == 0) ||
     120        1750 :                              (ix == 0 && iy == _ny - 1) || (ix == _nx - 1 && iy == _ny - 1);
     121         939 :       const bool is_edge = (ix == 0 || iy == 0 || ix == _nx - 1 || iy == _ny - 1);
     122             : 
     123         939 :       if (is_corner)
     124         230 :         _subch_type[i_ch] = EChannelType::CORNER;
     125         709 :       else if (is_edge)
     126         426 :         _subch_type[i_ch] = EChannelType::EDGE;
     127             :       else
     128         283 :         _subch_type[i_ch] = EChannelType::CENTER;
     129             : 
     130             :       // Set the subchannel positions so that the center of the assembly is the zero point.
     131         939 :       const Real offset_x = (_nx - 1) * _pitch / 2.0;
     132         939 :       const Real offset_y = (_ny - 1) * _pitch / 2.0;
     133         939 :       _subchannel_position[i_ch][0] = _pitch * ix - offset_x;
     134         939 :       _subchannel_position[i_ch][1] = _pitch * iy - offset_y;
     135             :     }
     136          71 : }
     137             : 
     138             : void
     139         533 : SCMDetailedQuadAssemblyMeshGenerator::generatePin(std::unique_ptr<MeshBase> & mesh_base,
     140             :                                                   const Point & center)
     141             : {
     142         533 :   const Real dalpha = 360. / _num_sectors;
     143         533 :   const Real radius = _pin_diameter / 2.;
     144             : 
     145             :   // Add a center node and radial boundary nodes on each axial level so each pin is discretized into
     146             :   // triangular prism sectors.
     147             :   std::vector<std::vector<Node *>> nodes;
     148         533 :   nodes.resize(_n_cells + 1);
     149       21098 :   for (unsigned int k = 0; k < _n_cells + 1; k++)
     150             :   {
     151       20565 :     const Real elev = _z_grid[k];
     152       20565 :     nodes[k].push_back(mesh_base->add_point(Point(center(0), center(1), elev)));
     153             :     Real alpha = 0.;
     154      349605 :     for (unsigned int i = 0; i < _num_sectors; i++, alpha += dalpha)
     155             :     {
     156      329040 :       const Real dx = radius * std::cos(alpha * M_PI / 180.);
     157      329040 :       const Real dy = radius * std::sin(alpha * M_PI / 180.);
     158      329040 :       nodes[k].push_back(mesh_base->add_point(Point(center(0) + dx, center(1) + dy, elev)));
     159             :     }
     160             :   }
     161             : 
     162             :   // Add the pin volume elements, linking matching radial sectors between adjacent axial levels.
     163       20565 :   for (unsigned int k = 0; k < _n_cells; k++)
     164      340544 :     for (unsigned int i = 0; i < _num_sectors; i++)
     165             :     {
     166      320512 :       Elem * elem = mesh_base->add_elem(std::make_unique<Prism6>());
     167      320512 :       elem->subdomain_id() = _pin_block_id;
     168      320512 :       elem->set_id(_elem_id++);
     169             :       const unsigned int ctr_idx = 0;
     170      320512 :       const unsigned int idx1 = (i % _num_sectors) + 1;
     171      320512 :       const unsigned int idx2 = ((i + 1) % _num_sectors) + 1;
     172      320512 :       elem->set_node(0, nodes[k][ctr_idx]);
     173      320512 :       elem->set_node(1, nodes[k][idx1]);
     174      320512 :       elem->set_node(2, nodes[k][idx2]);
     175      320512 :       elem->set_node(3, nodes[k + 1][ctr_idx]);
     176      320512 :       elem->set_node(4, nodes[k + 1][idx1]);
     177      320512 :       elem->set_node(5, nodes[k + 1][idx2]);
     178             :     }
     179         533 : }
     180             : 
     181             : std::unique_ptr<MeshBase>
     182          71 : SCMDetailedQuadAssemblyMeshGenerator::generate()
     183             : {
     184          71 :   auto mesh_base = buildMeshBaseObject();
     185             :   BoundaryInfo & boundary_info = mesh_base->get_boundary_info();
     186          71 :   mesh_base->set_spatial_dimension(3);
     187             : 
     188             :   // Define the resolution (the number of points used to represent a circle). This must be
     189             :   // divisible by 4.
     190          71 :   const unsigned int n_pins = (_nx - 1) * (_ny - 1);
     191             : 
     192             :   const unsigned int theta_res = 16;
     193             :   // Compute the number of points needed to represent one quarter of a circle.
     194             :   const unsigned int points_per_quad = theta_res / 4 + 1;
     195             : 
     196             :   // Compute the points needed to represent one axial cross-flow of a subchannel. For the center
     197             :   // subchannel there is one center point plus the points from 4 intersecting circles. For the
     198             :   // corner subchannel there is one center point plus the points from 1 intersecting circle plus 3
     199             :   // corners. For the side subchannel there is one center point plus the points from 2 intersecting
     200             :   // circles plus 2 corners.
     201             :   const unsigned int points_per_center = points_per_quad * 4 + 1;
     202             :   const unsigned int points_per_corner = points_per_quad * 1 + 1 + 3;
     203             :   const unsigned int points_per_side = points_per_quad * 2 + 1 + 2;
     204             : 
     205             :   // Compute the number of Prism6 elements which combine to create each subchannel cross-section.
     206             :   const unsigned int elems_per_center = theta_res + 4;
     207             :   const unsigned int elems_per_corner = theta_res / 4 + 4;
     208             :   const unsigned int elems_per_side = theta_res / 2 + 4;
     209             : 
     210             :   // Specify the number and type of subchannels.
     211             :   unsigned int n_center, n_side, n_corner;
     212          71 :   if (_n_channels == 2)
     213             :   {
     214             :     n_corner = 0;
     215             :     n_side = _n_channels;
     216             :     n_center = _n_channels - n_side - n_corner;
     217             :   }
     218          58 :   else if (_n_channels > 2 && (_ny == 1 || _nx == 1))
     219             :   {
     220             :     n_corner = 0;
     221             :     n_side = 2;
     222          14 :     n_center = _n_channels - n_side - n_corner;
     223             :   }
     224             :   else
     225             :   {
     226             :     n_corner = 4;
     227          44 :     n_side = 2 * (_ny - 2) + 2 * (_nx - 2);
     228          44 :     n_center = _n_channels - n_side - n_corner;
     229             :   }
     230             : 
     231          71 :   const unsigned int points_per_level =
     232          71 :       n_corner * points_per_corner + n_side * points_per_side + n_center * points_per_center;
     233          71 :   const unsigned int elems_per_level =
     234          71 :       n_corner * elems_per_corner + n_side * elems_per_side + n_center * elems_per_center;
     235             : 
     236             :   // Pin centers are generated for 2x2 and larger quad assemblies.
     237             :   std::vector<Point> pin_centers;
     238          71 :   if (n_pins > 0)
     239          44 :     QuadSubChannelMesh::generatePinCenters(_nx, _ny, _pitch, 0, pin_centers);
     240             : 
     241             :   const unsigned int pin_points =
     242          44 :       n_pins > 0 ? (_n_cells + 1) * (_num_sectors + 1) * pin_centers.size() : 0;
     243          71 :   const unsigned int pin_elems = n_pins > 0 ? _n_cells * _num_sectors * pin_centers.size() : 0;
     244             : 
     245          71 :   mesh_base->reserve_nodes(points_per_level * (_n_cells + 1) + pin_points);
     246          71 :   mesh_base->reserve_elem(elems_per_level * _n_cells + pin_elems);
     247             : 
     248             :   // Build an array of points arranged in a circle on the xy-plane. The last and first node overlap.
     249          71 :   const Real radius = _pin_diameter / 2.0;
     250             :   std::array<Point, theta_res + 1> circle_points;
     251             :   {
     252             :     Real theta = 0;
     253        1278 :     for (unsigned int i = 0; i < theta_res + 1; i++)
     254             :     {
     255        1207 :       circle_points[i](0) = radius * std::cos(theta);
     256        1207 :       circle_points[i](1) = radius * std::sin(theta);
     257        1207 :       theta += 2 * M_PI / theta_res;
     258             :     }
     259             :   }
     260             : 
     261             :   // Define "quadrant center" reference points. These will be the centers of the 4 circles that
     262             :   // represent the fuel pins. These centers are offset slightly so that in the final mesh, there is
     263             :   // a tiny gap between neighboring subchannel cells. That allows us to easily map a solution to
     264             :   // this detailed mesh with a nearest-neighbor search.
     265             :   const Real shrink_factor = 0.99999;
     266             :   std::array<Point, 4> quadrant_centers;
     267          71 :   quadrant_centers[0] = Point(_pitch * 0.5 * shrink_factor, _pitch * 0.5 * shrink_factor, 0);
     268          71 :   quadrant_centers[1] = Point(-_pitch * 0.5 * shrink_factor, _pitch * 0.5 * shrink_factor, 0);
     269          71 :   quadrant_centers[2] = Point(-_pitch * 0.5 * shrink_factor, -_pitch * 0.5 * shrink_factor, 0);
     270          71 :   quadrant_centers[3] = Point(_pitch * 0.5 * shrink_factor, -_pitch * 0.5 * shrink_factor, 0);
     271             : 
     272             :   const unsigned int m = theta_res / 4;
     273             :   // Build an array of points that represent a cross-section of a center subchannel cell. The points
     274             :   // are ordered in this fashion:
     275             :   //     4   3
     276             :   // 6 5       2 1
     277             :   //       0
     278             :   // 7 8       * *
     279             :   //     9   *
     280             :   std::array<Point, points_per_center> center_points;
     281             :   {
     282             :     unsigned int start;
     283         355 :     for (unsigned int i = 0; i < 4; i++)
     284             :     {
     285         284 :       if (i == 0)
     286             :         start = 3 * m;
     287         213 :       if (i == 1)
     288             :         start = 4 * m;
     289         213 :       if (i == 2)
     290             :         start = 1 * m;
     291         213 :       if (i == 3)
     292             :         start = 2 * m;
     293        1704 :       for (unsigned int ii = 0; ii < points_per_quad; ii++)
     294             :       {
     295        1420 :         auto c_pt = circle_points[start - ii];
     296        1420 :         center_points[i * points_per_quad + ii + 1] = quadrant_centers[i] + c_pt;
     297             :       }
     298             :     }
     299             :   }
     300             : 
     301             :   // Build an array of points that represent a cross-section of a top left corner subchannel cell.
     302             :   // The points are ordered in this fashion:
     303             :   // 5            4
     304             :   //
     305             :   //       0
     306             :   //           2  3
     307             :   // 6       1
     308             :   std::array<Point, points_per_corner> tl_corner_points;
     309             :   {
     310         426 :     for (unsigned int ii = 0; ii < points_per_quad; ii++)
     311             :     {
     312         355 :       auto c_pt = circle_points[2 * m - ii];
     313         355 :       tl_corner_points[ii + 1] = quadrant_centers[3] + c_pt;
     314             :     }
     315          71 :     tl_corner_points[points_per_quad + 1] = Point(_pitch * 0.5 * shrink_factor, _side_gap, 0);
     316          71 :     tl_corner_points[points_per_quad + 2] = Point(-_side_gap, _side_gap, 0);
     317          71 :     tl_corner_points[points_per_quad + 3] = Point(-_side_gap, -_pitch * 0.5 * shrink_factor, 0);
     318             :   }
     319             : 
     320             :   // Build an array of points that represent a cross-section of a top right corner subchannel cell.
     321             :   // The points are ordered in this fashion:
     322             :   // 6            5
     323             :   //
     324             :   //       0
     325             :   // 1 2
     326             :   //    3         4
     327             :   std::array<Point, points_per_corner> tr_corner_points;
     328             :   {
     329         426 :     for (unsigned int ii = 0; ii < points_per_quad; ii++)
     330             :     {
     331         355 :       auto c_pt = circle_points[m - ii];
     332         355 :       tr_corner_points[ii + 1] = quadrant_centers[2] + c_pt;
     333             :     }
     334          71 :     tr_corner_points[points_per_quad + 1] = Point(_side_gap, -_pitch * 0.5 * shrink_factor, 0);
     335          71 :     tr_corner_points[points_per_quad + 2] = Point(_side_gap, _side_gap, 0);
     336          71 :     tr_corner_points[points_per_quad + 3] = Point(-_pitch * 0.5 * shrink_factor, _side_gap, 0);
     337             :   }
     338             : 
     339             :   // Build an array of points that represent a cross-section of a bottom left corner subchannel
     340             :   // cell. The points are ordered in this fashion:
     341             :   // 4       3
     342             :   //           2  1
     343             :   //       0
     344             :   //
     345             :   // 5            6
     346             :   std::array<Point, points_per_corner> bl_corner_points;
     347             :   {
     348         426 :     for (unsigned int ii = 0; ii < points_per_quad; ii++)
     349             :     {
     350         355 :       auto c_pt = circle_points[3 * m - ii];
     351         355 :       bl_corner_points[ii + 1] = quadrant_centers[0] + c_pt;
     352             :     }
     353          71 :     bl_corner_points[points_per_quad + 1] = Point(-_side_gap, _pitch * 0.5 * shrink_factor, 0);
     354          71 :     bl_corner_points[points_per_quad + 2] = Point(-_side_gap, -_side_gap, 0);
     355          71 :     bl_corner_points[points_per_quad + 3] = Point(_pitch * 0.5 * shrink_factor, -_side_gap, 0);
     356             :   }
     357             : 
     358             :   // Build an array of points that represent a cross-section of a bottom right corner subchannel
     359             :   // cell. The points are ordered in this fashion:
     360             :   //    1        6
     361             :   // 3 2
     362             :   //       0
     363             :   //
     364             :   // 4           5
     365             :   std::array<Point, points_per_corner> br_corner_points;
     366             :   {
     367         426 :     for (unsigned int ii = 0; ii < points_per_quad; ii++)
     368             :     {
     369         355 :       auto c_pt = circle_points[4 * m - ii];
     370         355 :       br_corner_points[ii + 1] = quadrant_centers[1] + c_pt;
     371             :     }
     372          71 :     br_corner_points[points_per_quad + 1] = Point(-_pitch * 0.5 * shrink_factor, -_side_gap, 0);
     373          71 :     br_corner_points[points_per_quad + 2] = Point(_side_gap, -_side_gap, 0);
     374          71 :     br_corner_points[points_per_quad + 3] = Point(_side_gap, _pitch * 0.5 * shrink_factor, 0);
     375             :   }
     376             : 
     377             :   // Build an array of points that represent a cross-section of a top side subchannel cell. The
     378             :   // points are ordered in this fashion:
     379             :   // 8            7
     380             :   //
     381             :   //       0
     382             :   // 1 2        5 6
     383             :   //    3     4
     384             :   std::array<Point, points_per_side> top_points;
     385             :   {
     386         426 :     for (unsigned int ii = 0; ii < points_per_quad; ii++)
     387             :     {
     388         355 :       auto c_pt = circle_points[m - ii];
     389         355 :       top_points[ii + 1] = quadrant_centers[2] + c_pt;
     390             :     }
     391         426 :     for (unsigned int ii = 0; ii < points_per_quad; ii++)
     392             :     {
     393         355 :       auto c_pt = circle_points[2 * m - ii];
     394         355 :       top_points[points_per_quad + ii + 1] = quadrant_centers[3] + c_pt;
     395             :     }
     396          71 :     top_points[2 * points_per_quad + 1] = Point(_pitch * 0.5 * shrink_factor, _side_gap, 0);
     397          71 :     top_points[2 * points_per_quad + 2] = Point(-_pitch * 0.5 * shrink_factor, _side_gap, 0);
     398             :   }
     399             : 
     400             :   // Build an array of points that represent a cross-section of a left side subchannel cell. The
     401             :   // points are ordered in this fashion:
     402             :   // 7        6
     403             :   //            5 4
     404             :   //      0
     405             :   //            2 3
     406             :   // 8        1
     407             :   std::array<Point, points_per_side> left_points;
     408             :   {
     409         426 :     for (unsigned int ii = 0; ii < points_per_quad; ii++)
     410             :     {
     411         355 :       auto c_pt = circle_points[2 * m - ii];
     412         355 :       left_points[ii + 1] = quadrant_centers[3] + c_pt;
     413             :     }
     414         426 :     for (unsigned int ii = 0; ii < points_per_quad; ii++)
     415             :     {
     416         355 :       auto c_pt = circle_points[3 * m - ii];
     417         355 :       left_points[points_per_quad + ii + 1] = quadrant_centers[0] + c_pt;
     418             :     }
     419          71 :     left_points[2 * points_per_quad + 1] = Point(-_side_gap, _pitch * 0.5 * shrink_factor, 0);
     420          71 :     left_points[2 * points_per_quad + 2] = Point(-_side_gap, -_pitch * 0.5 * shrink_factor, 0);
     421             :   }
     422             : 
     423             :   // Build an array of points that represent a cross-section of a bottom side subchannel cell. The
     424             :   // points are ordered in this fashion:
     425             :   //    4    3
     426             :   // 6 5       2 1
     427             :   //       0
     428             :   //
     429             :   // 7           8
     430             :   std::array<Point, points_per_side> bottom_points;
     431             :   {
     432         426 :     for (unsigned int ii = 0; ii < points_per_quad; ii++)
     433             :     {
     434         355 :       auto c_pt = circle_points[3 * m - ii];
     435         355 :       bottom_points[ii + 1] = quadrant_centers[0] + c_pt;
     436             :     }
     437         426 :     for (unsigned int ii = 0; ii < points_per_quad; ii++)
     438             :     {
     439         355 :       auto c_pt = circle_points[4 * m - ii];
     440         355 :       bottom_points[points_per_quad + ii + 1] = quadrant_centers[1] + c_pt;
     441             :     }
     442          71 :     bottom_points[2 * points_per_quad + 1] = Point(-_pitch * 0.5 * shrink_factor, -_side_gap, 0);
     443          71 :     bottom_points[2 * points_per_quad + 2] = Point(_pitch * 0.5 * shrink_factor, -_side_gap, 0);
     444             :   }
     445             : 
     446             :   // Build an array of points that represent a cross-section of a right side subchannel cell. The
     447             :   // points are ordered in this fashion:
     448             :   //    1        8
     449             :   // 3 2
     450             :   //       0
     451             :   // 4 5
     452             :   //   6         7
     453             :   std::array<Point, points_per_side> right_points;
     454             :   {
     455         426 :     for (unsigned int ii = 0; ii < points_per_quad; ii++)
     456             :     {
     457         355 :       auto c_pt = circle_points[4 * m - ii];
     458         355 :       right_points[ii + 1] = quadrant_centers[1] + c_pt;
     459             :     }
     460         426 :     for (unsigned int ii = 0; ii < points_per_quad; ii++)
     461             :     {
     462         355 :       auto c_pt = circle_points[m - ii];
     463         355 :       right_points[points_per_quad + ii + 1] = quadrant_centers[2] + c_pt;
     464             :     }
     465          71 :     right_points[2 * points_per_quad + 1] = Point(_side_gap, -_pitch * 0.5 * shrink_factor, 0);
     466          71 :     right_points[2 * points_per_quad + 2] = Point(_side_gap, _pitch * 0.5 * shrink_factor, 0);
     467             :   }
     468             : 
     469          71 :   if (_n_channels == 2)
     470             :   {
     471             :     // Special handling for the smallest line meshes, which contain only side subchannels.
     472             :     unsigned int node_id = 0;
     473          13 :     const Real offset_x = (_nx - 1) * _pitch / 2.0;
     474          13 :     const Real offset_y = (_ny - 1) * _pitch / 2.0;
     475          33 :     for (unsigned int iy = 0; iy < _ny; iy++)
     476             :     {
     477          20 :       Point y0 = {0, _pitch * iy - offset_y, 0};
     478          46 :       for (unsigned int ix = 0; ix < _nx; ix++)
     479             :       {
     480          26 :         Point x0 = {_pitch * ix - offset_x, 0, 0};
     481        1392 :         for (auto z : _z_grid)
     482             :         {
     483             :           Point z0{0, 0, z};
     484        1366 :           if (_nx == 1 && iy == 0) // vertical orientation
     485        1078 :             for (unsigned int i = 0; i < points_per_side; i++)
     486        1001 :               mesh_base->add_point(bottom_points[i] + x0 + y0 + z0, node_id++);
     487        1366 :           if (_nx == 1 && iy == 1) // vertical orientation
     488        1078 :             for (unsigned int i = 0; i < points_per_side; i++)
     489        1001 :               mesh_base->add_point(top_points[i] + x0 + y0 + z0, node_id++);
     490        1366 :           if (_ny == 1 && ix == 0) // horizontal orientation
     491        8484 :             for (unsigned int i = 0; i < points_per_side; i++)
     492        7878 :               mesh_base->add_point(left_points[i] + x0 + y0 + z0, node_id++);
     493        1366 :           if (_ny == 1 && ix == 1) // horizontal orientation
     494        8484 :             for (unsigned int i = 0; i < points_per_side; i++)
     495        7878 :               mesh_base->add_point(right_points[i] + x0 + y0 + z0, node_id++);
     496             :         }
     497             :       }
     498             :     }
     499             :   }
     500          58 :   else if (_n_channels > 2 && (_ny == 1 || _nx == 1))
     501             :   {
     502             :     // Line meshes larger than 2 channels have two side subchannels and center subchannels between
     503             :     // them.
     504             :     unsigned int node_id = 0;
     505          14 :     const Real offset_x = (_nx - 1) * _pitch / 2.0;
     506          14 :     const Real offset_y = (_ny - 1) * _pitch / 2.0;
     507          42 :     for (unsigned int iy = 0; iy < _ny; iy++)
     508             :     {
     509          28 :       Point y0 = {0, _pitch * iy - offset_y, 0};
     510          70 :       for (unsigned int ix = 0; ix < _nx; ix++)
     511             :       {
     512          42 :         Point x0 = {_pitch * ix - offset_x, 0, 0};
     513         504 :         for (auto z : _z_grid)
     514             :         {
     515             :           Point z0{0, 0, z};
     516         462 :           if (_nx == 1)
     517             :           {
     518         231 :             if (iy == 0)
     519        1078 :               for (unsigned int i = 0; i < points_per_side; i++)
     520        1001 :                 mesh_base->add_point(bottom_points[i] + x0 + y0 + z0, node_id++);
     521         154 :             else if (iy == _ny - 1)
     522        1078 :               for (unsigned int i = 0; i < points_per_side; i++)
     523        1001 :                 mesh_base->add_point(top_points[i] + x0 + y0 + z0, node_id++);
     524             :             else
     525        1694 :               for (unsigned int i = 0; i < points_per_center; i++)
     526        1617 :                 mesh_base->add_point(center_points[i] + x0 + y0 + z0, node_id++);
     527             :           }
     528         231 :           else if (_ny == 1)
     529             :           {
     530         231 :             if (ix == 0)
     531        1078 :               for (unsigned int i = 0; i < points_per_side; i++)
     532        1001 :                 mesh_base->add_point(left_points[i] + x0 + y0 + z0, node_id++);
     533         154 :             else if (ix == _nx - 1)
     534        1078 :               for (unsigned int i = 0; i < points_per_side; i++)
     535        1001 :                 mesh_base->add_point(right_points[i] + x0 + y0 + z0, node_id++);
     536             :             else
     537        1694 :               for (unsigned int i = 0; i < points_per_center; i++)
     538        1617 :                 mesh_base->add_point(center_points[i] + x0 + y0 + z0, node_id++);
     539             :           }
     540             :         }
     541             :       }
     542             :     }
     543             :   }
     544             :   else
     545             :   {
     546             :     // General 2D quad assemblies contain corner, side, and center subchannels.
     547             :     unsigned int node_id = 0;
     548          44 :     const Real offset_x = (_nx - 1) * _pitch / 2.0;
     549          44 :     const Real offset_y = (_ny - 1) * _pitch / 2.0;
     550         211 :     for (unsigned int iy = 0; iy < _ny; iy++)
     551             :     {
     552         167 :       Point y0 = {0, _pitch * iy - offset_y, 0};
     553        1038 :       for (unsigned int ix = 0; ix < _nx; ix++)
     554             :       {
     555         871 :         Point x0 = {_pitch * ix - offset_x, 0, 0};
     556         871 :         if (ix == 0 && iy == 0) // Bottom Left corner
     557        1349 :           for (auto z : _z_grid)
     558             :           {
     559             :             Point z0{0, 0, z};
     560       13050 :             for (unsigned int i = 0; i < points_per_corner; i++)
     561       11745 :               mesh_base->add_point(bl_corner_points[i] + x0 + y0 + z0, node_id++);
     562             :           }
     563         827 :         else if (ix == _nx - 1 && iy == 0) // Bottom right corner
     564        1349 :           for (auto z : _z_grid)
     565             :           {
     566             :             Point z0{0, 0, z};
     567       13050 :             for (unsigned int i = 0; i < points_per_corner; i++)
     568       11745 :               mesh_base->add_point(br_corner_points[i] + x0 + y0 + z0, node_id++);
     569             :           }
     570         783 :         else if (ix == 0 && iy == _ny - 1) // top Left corner
     571        1349 :           for (auto z : _z_grid)
     572             :           {
     573             :             Point z0{0, 0, z};
     574       13050 :             for (unsigned int i = 0; i < points_per_corner; i++)
     575       11745 :               mesh_base->add_point(tl_corner_points[i] + x0 + y0 + z0, node_id++);
     576             :           }
     577         739 :         else if (ix == _nx - 1 && iy == _ny - 1) // top right corner
     578        1349 :           for (auto z : _z_grid)
     579             :           {
     580             :             Point z0{0, 0, z};
     581       13050 :             for (unsigned int i = 0; i < points_per_corner; i++)
     582       11745 :               mesh_base->add_point(tr_corner_points[i] + x0 + y0 + z0, node_id++);
     583             :           }
     584         695 :         else if (ix == 0 && (iy != _ny - 1 || iy != 0)) // left side
     585        2879 :           for (auto z : _z_grid)
     586             :           {
     587             :             Point z0{0, 0, z};
     588       39200 :             for (unsigned int i = 0; i < points_per_side; i++)
     589       36400 :               mesh_base->add_point(left_points[i] + x0 + y0 + z0, node_id++);
     590             :           }
     591         616 :         else if (ix == _nx - 1 && (iy != _ny - 1 || iy != 0)) // right side
     592        2879 :           for (auto z : _z_grid)
     593             :           {
     594             :             Point z0{0, 0, z};
     595       39200 :             for (unsigned int i = 0; i < points_per_side; i++)
     596       36400 :               mesh_base->add_point(right_points[i] + x0 + y0 + z0, node_id++);
     597             :           }
     598         537 :         else if (iy == 0 && (ix != _nx - 1 || ix != 0)) // bottom side
     599        5279 :           for (auto z : _z_grid)
     600             :           {
     601             :             Point z0{0, 0, z};
     602       72128 :             for (unsigned int i = 0; i < points_per_side; i++)
     603       66976 :               mesh_base->add_point(bottom_points[i] + x0 + y0 + z0, node_id++);
     604             :           }
     605         410 :         else if (iy == _ny - 1 && (ix != _nx - 1 || ix != 0)) // top side
     606        5279 :           for (auto z : _z_grid)
     607             :           {
     608             :             Point z0{0, 0, z};
     609       72128 :             for (unsigned int i = 0; i < points_per_side; i++)
     610       66976 :               mesh_base->add_point(top_points[i] + x0 + y0 + z0, node_id++);
     611             :           }
     612             :         else // center
     613       11591 :           for (auto z : _z_grid)
     614             :           {
     615             :             Point z0{0, 0, z};
     616      248776 :             for (unsigned int i = 0; i < points_per_center; i++)
     617      237468 :               mesh_base->add_point(center_points[i] + x0 + y0 + z0, node_id++);
     618             :           }
     619             :       }
     620             :     }
     621             :   }
     622             : 
     623          71 :   if (_n_channels == 2)
     624             :   {
     625             :     // Build elements for the smallest line meshes.
     626          33 :     for (unsigned int iy = 0; iy < _ny; iy++)
     627          46 :       for (unsigned int ix = 0; ix < _nx; ix++)
     628             :       {
     629          26 :         const unsigned int i_ch = _nx * iy + ix;
     630        1366 :         for (unsigned int iz = 0; iz < _n_cells; iz++)
     631       17420 :           for (unsigned int i = 0; i < elems_per_side; i++)
     632             :           {
     633       16080 :             Elem * elem = mesh_base->add_elem(std::make_unique<Prism6>());
     634       16080 :             elem->subdomain_id() = _subchannel_block_id;
     635       16080 :             elem->set_id(_elem_id++);
     636             :             // index of the central node at base of cell
     637       16080 :             const unsigned int indx1 =
     638       16080 :                 iz * points_per_side + points_per_side * (_n_cells + 1) * i_ch;
     639       16080 :             const unsigned int indx2 =
     640             :                 (iz + 1) * points_per_side + points_per_side * (_n_cells + 1) * i_ch;
     641             :             const unsigned int elems_per_channel = elems_per_side;
     642       16080 :             elem->set_node(0, mesh_base->node_ptr(indx1));
     643       16080 :             elem->set_node(1, mesh_base->node_ptr(indx1 + i + 1));
     644       32160 :             elem->set_node(2,
     645       14740 :                            i != elems_per_channel - 1 ? mesh_base->node_ptr(indx1 + i + 2)
     646        1340 :                                                       : mesh_base->node_ptr(indx1 + 1));
     647       16080 :             elem->set_node(3, mesh_base->node_ptr(indx2));
     648       16080 :             elem->set_node(4, mesh_base->node_ptr(indx2 + i + 1));
     649       32160 :             elem->set_node(5,
     650       14740 :                            i != elems_per_channel - 1 ? mesh_base->node_ptr(indx2 + i + 2)
     651        1340 :                                                       : mesh_base->node_ptr(indx2 + 1));
     652             : 
     653       16080 :             if (iz == 0)
     654         312 :               boundary_info.add_side(elem, 0, 0);
     655       16080 :             if (iz == _n_cells - 1)
     656         312 :               boundary_info.add_side(elem, 4, 1);
     657             :           }
     658             :       }
     659             :   }
     660          58 :   else if (_n_channels > 2 && (_ny == 1 || _nx == 1))
     661             :   {
     662             :     // Build elements for 1xN and Nx1 line meshes.
     663             :     unsigned int number_of_corner = 0;
     664             :     unsigned int number_of_side = 0;
     665             :     unsigned int number_of_center = 0;
     666             :     unsigned int elems_per_channel = 0;
     667             :     unsigned int points_per_channel = 0;
     668          42 :     for (unsigned int iy = 0; iy < _ny; iy++)
     669          70 :       for (unsigned int ix = 0; ix < _nx; ix++)
     670             :       {
     671          42 :         const unsigned int i_ch = _nx * iy + ix;
     672             :         const auto subch_type = getSubchannelType(i_ch);
     673          42 :         if (subch_type == EChannelType::CORNER)
     674             :         {
     675          28 :           number_of_side++;
     676             :           elems_per_channel = elems_per_side;
     677             :           points_per_channel = points_per_side;
     678             :         }
     679          14 :         else if (subch_type == EChannelType::EDGE)
     680             :         {
     681          14 :           number_of_center++;
     682             :           elems_per_channel = elems_per_center;
     683             :           points_per_channel = points_per_center;
     684             :         }
     685             : 
     686         462 :         for (unsigned int iz = 0; iz < _n_cells; iz++)
     687             :         {
     688         420 :           const unsigned int elapsed_points =
     689         420 :               number_of_corner * points_per_corner * (_n_cells + 1) +
     690         420 :               number_of_side * points_per_side * (_n_cells + 1) +
     691             :               number_of_center * points_per_center * (_n_cells + 1) -
     692         420 :               points_per_channel * (_n_cells + 1);
     693             :           // index of the central node at base of cell
     694         420 :           const unsigned int indx1 = iz * points_per_channel + elapsed_points;
     695         420 :           const unsigned int indx2 = (iz + 1) * points_per_channel + elapsed_points;
     696             : 
     697        6580 :           for (unsigned int i = 0; i < elems_per_channel; i++)
     698             :           {
     699        6160 :             Elem * elem = mesh_base->add_elem(std::make_unique<Prism6>());
     700        6160 :             elem->subdomain_id() = _subchannel_block_id;
     701        6160 :             elem->set_id(_elem_id++);
     702        6160 :             elem->set_node(0, mesh_base->node_ptr(indx1));
     703        6160 :             elem->set_node(1, mesh_base->node_ptr(indx1 + i + 1));
     704        6160 :             elem->set_node(2,
     705        6160 :                            i != elems_per_channel - 1 ? mesh_base->node_ptr(indx1 + i + 2)
     706         420 :                                                       : mesh_base->node_ptr(indx1 + 1));
     707        6160 :             elem->set_node(3, mesh_base->node_ptr(indx2));
     708        6160 :             elem->set_node(4, mesh_base->node_ptr(indx2 + i + 1));
     709       12320 :             elem->set_node(5,
     710        5740 :                            i != elems_per_channel - 1 ? mesh_base->node_ptr(indx2 + i + 2)
     711         420 :                                                       : mesh_base->node_ptr(indx2 + 1));
     712             : 
     713        6160 :             if (iz == 0)
     714         616 :               boundary_info.add_side(elem, 0, 0);
     715        6160 :             if (iz == _n_cells - 1)
     716         616 :               boundary_info.add_side(elem, 4, 1);
     717             :           }
     718             :         }
     719             :       }
     720             :   }
     721             :   else
     722             :   {
     723             :     // Build elements for general 2D quad assemblies.
     724             :     unsigned int number_of_corner = 0;
     725             :     unsigned int number_of_side = 0;
     726             :     unsigned int number_of_center = 0;
     727             :     unsigned int elems_per_channel = 0;
     728             :     unsigned int points_per_channel = 0;
     729         211 :     for (unsigned int iy = 0; iy < _ny; iy++)
     730        1038 :       for (unsigned int ix = 0; ix < _nx; ix++)
     731             :       {
     732         871 :         const unsigned int i_ch = _nx * iy + ix;
     733             :         const auto subch_type = getSubchannelType(i_ch);
     734         871 :         if (subch_type == EChannelType::CORNER)
     735             :         {
     736         176 :           number_of_corner++;
     737             :           elems_per_channel = elems_per_corner;
     738             :           points_per_channel = points_per_corner;
     739             :         }
     740         695 :         else if (subch_type == EChannelType::EDGE)
     741             :         {
     742         412 :           number_of_side++;
     743             :           elems_per_channel = elems_per_side;
     744             :           points_per_channel = points_per_side;
     745             :         }
     746             :         else
     747             :         {
     748         283 :           number_of_center++;
     749             :           elems_per_channel = elems_per_center;
     750             :           points_per_channel = points_per_center;
     751             :         }
     752             : 
     753       32432 :         for (unsigned int iz = 0; iz < _n_cells; iz++)
     754             :         {
     755       31561 :           const unsigned int elapsed_points =
     756       31561 :               number_of_corner * points_per_corner * (_n_cells + 1) +
     757       31561 :               number_of_side * points_per_side * (_n_cells + 1) +
     758             :               number_of_center * points_per_center * (_n_cells + 1) -
     759       31561 :               points_per_channel * (_n_cells + 1);
     760             :           // index of the central node at base of cell
     761       31561 :           const unsigned int indx1 = iz * points_per_channel + elapsed_points;
     762       31561 :           const unsigned int indx2 = (iz + 1) * points_per_channel + elapsed_points;
     763             : 
     764      478317 :           for (unsigned int i = 0; i < elems_per_channel; i++)
     765             :           {
     766      446756 :             Elem * elem = mesh_base->add_elem(std::make_unique<Prism6>());
     767      446756 :             elem->subdomain_id() = _subchannel_block_id;
     768      446756 :             elem->set_id(_elem_id++);
     769      446756 :             elem->set_node(0, mesh_base->node_ptr(indx1));
     770      446756 :             elem->set_node(1, mesh_base->node_ptr(indx1 + i + 1));
     771      446756 :             elem->set_node(2,
     772      446756 :                            i != elems_per_channel - 1 ? mesh_base->node_ptr(indx1 + i + 2)
     773       31561 :                                                       : mesh_base->node_ptr(indx1 + 1));
     774      446756 :             elem->set_node(3, mesh_base->node_ptr(indx2));
     775      446756 :             elem->set_node(4, mesh_base->node_ptr(indx2 + i + 1));
     776      893512 :             elem->set_node(5,
     777      415195 :                            i != elems_per_channel - 1 ? mesh_base->node_ptr(indx2 + i + 2)
     778       31561 :                                                       : mesh_base->node_ptr(indx2 + 1));
     779             : 
     780      446756 :             if (iz == 0)
     781       12012 :               boundary_info.add_side(elem, 0, 0);
     782      446756 :             if (iz == _n_cells - 1)
     783       12012 :               boundary_info.add_side(elem, 4, 1);
     784             :           }
     785             :         }
     786             :       }
     787             :   }
     788             : 
     789          71 :   if (n_pins > 0)
     790         577 :     for (auto & ctr : pin_centers)
     791         533 :       generatePin(mesh_base, ctr);
     792             : 
     793          71 :   boundary_info.sideset_name(0) = "inlet";
     794          71 :   boundary_info.sideset_name(1) = "outlet";
     795          71 :   mesh_base->subdomain_name(_subchannel_block_id) = "subchannel";
     796          71 :   if (n_pins > 0)
     797          44 :     mesh_base->subdomain_name(_pin_block_id) = "fuel_pins";
     798          71 :   mesh_base->prepare_for_use();
     799             : 
     800          71 :   return mesh_base;
     801          71 : }

Generated by: LCOV version 1.14