LCOV - code coverage report
Current view: top level - src/meshgenerators - SCMDetailedTriSubChannelMeshGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose subchannel: #32971 (54bef8) with base c6cf66 Lines: 396 415 95.4 %
Date: 2026-05-29 20:40:47 Functions: 4 5 80.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 "SCMDetailedTriSubChannelMeshGenerator.h"
      11             : #include "TriSubChannelMesh.h"
      12             : #include <array>
      13             : #include <cmath>
      14             : #include "libmesh/cell_prism6.h"
      15             : #include "libmesh/unstructured_mesh.h"
      16             : 
      17             : registerMooseObject("SubChannelApp", SCMDetailedTriSubChannelMeshGenerator);
      18             : 
      19             : InputParameters
      20          80 : SCMDetailedTriSubChannelMeshGenerator::validParams()
      21             : {
      22          80 :   InputParameters params = MeshGenerator::validParams();
      23          80 :   params.addClassDescription(
      24             :       "Creates a detailed mesh of subchannels in a triangular lattice arrangement");
      25         160 :   params.addRequiredParam<Real>("pitch", "Pitch [m]");
      26         160 :   params.addRequiredParam<Real>("pin_diameter", "Rod diameter [m]");
      27         160 :   params.addParam<Real>("unheated_length_entry", 0.0, "Unheated length at entry [m]");
      28         160 :   params.addRequiredParam<Real>("heated_length", "Heated length [m]");
      29         160 :   params.addParam<Real>("unheated_length_exit", 0.0, "Unheated length at exit [m]");
      30         160 :   params.addRequiredParam<unsigned int>("nrings", "Number of fuel Pin rings per assembly [-]");
      31         160 :   params.addRequiredParam<Real>("flat_to_flat",
      32             :                                 "Flat to flat distance for the hexagonal assembly [m]");
      33         160 :   params.addParam<unsigned int>("block_id", 0, "Block ID used for the mesh subdomain.");
      34         160 :   params.addRequiredParam<unsigned int>("n_cells", "The number of cells in the axial direction");
      35         160 :   params.addParam<bool>("verbose_flag", false, "Flag to print out the mesh coordinates.");
      36          80 :   return params;
      37           0 : }
      38             : 
      39          40 : SCMDetailedTriSubChannelMeshGenerator::SCMDetailedTriSubChannelMeshGenerator(
      40          40 :     const InputParameters & parameters)
      41             :   : MeshGenerator(parameters),
      42          40 :     _unheated_length_entry(getParam<Real>("unheated_length_entry")),
      43          80 :     _heated_length(getParam<Real>("heated_length")),
      44          80 :     _unheated_length_exit(getParam<Real>("unheated_length_exit")),
      45          80 :     _pitch(getParam<Real>("pitch")),
      46          80 :     _pin_diameter(getParam<Real>("pin_diameter")),
      47          80 :     _n_rings(getParam<unsigned int>("nrings")),
      48          80 :     _flat_to_flat(getParam<Real>("flat_to_flat")),
      49          80 :     _block_id(getParam<unsigned int>("block_id")),
      50          80 :     _n_cells(getParam<unsigned int>("n_cells")),
      51         120 :     _verbose(getParam<bool>("verbose_flag"))
      52             : {
      53          40 :   Real L = _unheated_length_entry + _heated_length + _unheated_length_exit;
      54          40 :   Real dz = L / _n_cells;
      55        1420 :   for (unsigned int i = 0; i < _n_cells + 1; i++)
      56        1380 :     _z_grid.push_back(dz * i);
      57             : 
      58             :   // x coordinate for the first position
      59             :   Real x0 = 0.0;
      60             :   // y coordinate for the first position
      61             :   Real y0 = 0.0;
      62             :   // x coordinate for the second position
      63             :   Real x1 = 0.0;
      64             :   // y coordinate for the second position dummy variable
      65             :   Real y1 = 0.0;
      66             :   // dummy variable
      67             :   Real a1 = 0.0;
      68             :   // dummy variable
      69             :   Real a2 = 0.0;
      70             :   // average x coordinate
      71             :   Real avg_coor_x = 0.0;
      72             :   // average y coordinate
      73             :   Real avg_coor_y = 0.0;
      74             :   // distance between two points
      75             :   Real dist = 0.0;
      76             :   // distance between two points
      77             :   Real dist0 = 0.0;
      78             :   // the indicator used while setting _gap_to_chan_map array
      79             :   std::vector<std::pair<unsigned int, unsigned int>> gap_fill;
      80          40 :   TriSubChannelMesh::pinPositions(_pin_position, _n_rings, _pitch, Point(0, 0));
      81          40 :   _nrods = _pin_position.size();
      82             :   // assign the pins to the corresponding rings
      83             :   unsigned int k = 0; // initialize the fuel Pin counter index
      84          40 :   _pins_in_rings.resize(_n_rings);
      85          40 :   _pins_in_rings[0].push_back(k++);
      86         126 :   for (unsigned int i = 1; i < _n_rings; i++)
      87         914 :     for (unsigned int j = 0; j < i * 6; j++)
      88         828 :       _pins_in_rings[i].push_back(k++);
      89             :   //  Given the number of pins and number of fuel Pin rings, the number of subchannels can be
      90             :   //  computed as follows:
      91             :   unsigned int chancount = 0.0;
      92             :   // Summing internal channels
      93         126 :   for (unsigned int j = 0; j < _n_rings - 1; j++)
      94          86 :     chancount += j * 6;
      95             :   // Adding external channels to the total count
      96          40 :   _n_channels = chancount + _nrods - 1 + (_n_rings - 1) * 6 + 6;
      97             : 
      98             :   // Utils for building the mesh
      99          40 :   _chan_to_pin_map.resize(_n_channels);
     100          40 :   _subch_type.resize(_n_channels);
     101          40 :   _subchannel_position.resize(_n_channels);
     102             : 
     103        1936 :   for (unsigned int i = 0; i < _n_channels; i++)
     104             :   {
     105        1896 :     _subchannel_position[i].reserve(3);
     106        7584 :     for (unsigned int j = 0; j < 3; j++)
     107             :     {
     108        5688 :       _subchannel_position.at(i).push_back(0.0);
     109             :     }
     110             :   }
     111             : 
     112             :   // create the subchannels
     113             :   k = 0; // initialize the subchannel counter index
     114         126 :   for (unsigned int i = 1; i < _n_rings; i++)
     115             :   {
     116             :     // find the closest Pin at back ring
     117         914 :     for (unsigned int j = 0; j < _pins_in_rings[i].size(); j++)
     118             :     {
     119         828 :       if (j == _pins_in_rings[i].size() - 1)
     120             :       {
     121          86 :         _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
     122          86 :         _chan_to_pin_map[k].push_back(_pins_in_rings[i][0]);
     123          86 :         avg_coor_x =
     124          86 :             0.5 * (_pin_position[_pins_in_rings[i][j]](0) + _pin_position[_pins_in_rings[i][0]](0));
     125          86 :         avg_coor_y =
     126          86 :             0.5 * (_pin_position[_pins_in_rings[i][j]](1) + _pin_position[_pins_in_rings[i][0]](1));
     127             :       }
     128             :       else
     129             :       {
     130         742 :         _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
     131         742 :         _chan_to_pin_map[k].push_back(_pins_in_rings[i][j + 1]);
     132         742 :         avg_coor_x = 0.5 * (_pin_position[_pins_in_rings[i][j]](0) +
     133         742 :                             _pin_position[_pins_in_rings[i][j + 1]](0));
     134         742 :         avg_coor_y = 0.5 * (_pin_position[_pins_in_rings[i][j]](1) +
     135         742 :                             _pin_position[_pins_in_rings[i][j + 1]](1));
     136             :       }
     137             :       dist0 = 1.0e+5;
     138         828 :       _chan_to_pin_map[k].push_back(_pins_in_rings[i - 1][0]);
     139        5244 :       for (unsigned int l = 0; l < _pins_in_rings[i - 1].size(); l++)
     140             :       {
     141        4416 :         dist = std::sqrt(pow(_pin_position[_pins_in_rings[i - 1][l]](0) - avg_coor_x, 2) +
     142        4416 :                          pow(_pin_position[_pins_in_rings[i - 1][l]](1) - avg_coor_y, 2));
     143             : 
     144        4416 :         if (dist < dist0)
     145             :         {
     146        1976 :           _chan_to_pin_map[k][2] = _pins_in_rings[i - 1][l];
     147             :           dist0 = dist;
     148             :         }
     149             :       }
     150         828 :       _subch_type[k] = EChannelType::CENTER;
     151         828 :       _orientation_map.insert(std::make_pair(k, 0.0));
     152         828 :       k = k + 1;
     153             :     }
     154             : 
     155             :     // find the closest Pin at front ring
     156         914 :     for (unsigned int j = 0; j < _pins_in_rings[i].size(); j++)
     157             :     {
     158         828 :       if (j == _pins_in_rings[i].size() - 1)
     159             :       {
     160          86 :         _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
     161          86 :         _chan_to_pin_map[k].push_back(_pins_in_rings[i][0]);
     162          86 :         avg_coor_x =
     163          86 :             0.5 * (_pin_position[_pins_in_rings[i][j]](0) + _pin_position[_pins_in_rings[i][0]](0));
     164          86 :         avg_coor_y =
     165          86 :             0.5 * (_pin_position[_pins_in_rings[i][j]](1) + _pin_position[_pins_in_rings[i][0]](1));
     166             :       }
     167             :       else
     168             :       {
     169         742 :         _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
     170         742 :         _chan_to_pin_map[k].push_back(_pins_in_rings[i][j + 1]);
     171         742 :         avg_coor_x = 0.5 * (_pin_position[_pins_in_rings[i][j]](0) +
     172         742 :                             _pin_position[_pins_in_rings[i][j + 1]](0));
     173         742 :         avg_coor_y = 0.5 * (_pin_position[_pins_in_rings[i][j]](1) +
     174         742 :                             _pin_position[_pins_in_rings[i][j + 1]](1));
     175             :       }
     176             :       // if the outermost ring, set the edge subchannels first... then the corner subchannels
     177         828 :       if (i == _n_rings - 1)
     178             :       {
     179             :         // add  edges
     180         516 :         _subch_type[k] = EChannelType::EDGE; // an edge subchannel is created
     181         516 :         k = k + 1;
     182         516 :         if (j % i == 0)
     183             :         {
     184             :           // corner subchannel
     185         240 :           _chan_to_pin_map[k].push_back(_pins_in_rings[i][j]);
     186         240 :           _subch_type[k] = EChannelType::CORNER;
     187         240 :           k = k + 1;
     188             :         }
     189             :         // if not the outer most ring
     190             :       }
     191             :       else
     192             :       {
     193             :         dist0 = 1.0e+5;
     194         312 :         _chan_to_pin_map[k].push_back(_pins_in_rings[i + 1][0]);
     195        4488 :         for (unsigned int l = 0; l < _pins_in_rings[i + 1].size(); l++)
     196             :         {
     197        4176 :           dist = std::sqrt(pow(_pin_position[_pins_in_rings[i + 1][l]](0) - avg_coor_x, 2) +
     198        4176 :                            pow(_pin_position[_pins_in_rings[i + 1][l]](1) - avg_coor_y, 2));
     199        4176 :           if (dist < dist0)
     200             :           {
     201        1512 :             _chan_to_pin_map[k][2] = _pins_in_rings[i + 1][l];
     202             :             dist0 = dist;
     203             :           }
     204             :         }
     205         312 :         _subch_type[k] = EChannelType::CENTER;
     206         312 :         _orientation_map.insert(std::make_pair(k, libMesh::pi));
     207         312 :         k = k + 1;
     208             :       }
     209             :     }
     210             :   }
     211             : 
     212        1936 :   for (auto & pin : _chan_to_pin_map)
     213             :     pin.shrink_to_fit();
     214             : 
     215             :   // set the subchannel positions
     216          40 :   Real _duct_to_pin_gap =
     217          40 :       0.5 * (_flat_to_flat - (_n_rings - 1) * _pitch * std::sqrt(3.0) - _pin_diameter);
     218        1936 :   for (unsigned int i = 0; i < _n_channels; i++)
     219             :   {
     220        1896 :     if (_subch_type[i] == EChannelType::CENTER)
     221             :     {
     222        1140 :       _subchannel_position[i][0] =
     223        1140 :           (_pin_position[_chan_to_pin_map[i][0]](0) + _pin_position[_chan_to_pin_map[i][1]](0) +
     224        1140 :            _pin_position[_chan_to_pin_map[i][2]](0)) /
     225             :           3.0;
     226        1140 :       _subchannel_position[i][1] =
     227        1140 :           (_pin_position[_chan_to_pin_map[i][0]](1) + _pin_position[_chan_to_pin_map[i][1]](1) +
     228        1140 :            _pin_position[_chan_to_pin_map[i][2]](1)) /
     229             :           3.0;
     230             :     }
     231         756 :     else if (_subch_type[i] == EChannelType::EDGE)
     232             :     {
     233       26076 :       for (unsigned int j = 0; j < _n_channels; j++)
     234             :       {
     235       25560 :         if (_subch_type[j] == EChannelType::CENTER &&
     236       15624 :             ((_chan_to_pin_map[i][0] == _chan_to_pin_map[j][0] &&
     237         516 :               _chan_to_pin_map[i][1] == _chan_to_pin_map[j][1]) ||
     238       15108 :              (_chan_to_pin_map[i][0] == _chan_to_pin_map[j][1] &&
     239         516 :               _chan_to_pin_map[i][1] == _chan_to_pin_map[j][0])))
     240             :         {
     241         516 :           x0 = _pin_position[_chan_to_pin_map[j][2]](0);
     242         516 :           y0 = _pin_position[_chan_to_pin_map[j][2]](1);
     243             :         }
     244       25044 :         else if (_subch_type[j] == EChannelType::CENTER &&
     245       15108 :                  ((_chan_to_pin_map[i][0] == _chan_to_pin_map[j][0] &&
     246           0 :                    _chan_to_pin_map[i][1] == _chan_to_pin_map[j][2]) ||
     247       15108 :                   (_chan_to_pin_map[i][0] == _chan_to_pin_map[j][2] &&
     248         276 :                    _chan_to_pin_map[i][1] == _chan_to_pin_map[j][0])))
     249             :         {
     250           0 :           x0 = _pin_position[_chan_to_pin_map[j][1]](0);
     251           0 :           y0 = _pin_position[_chan_to_pin_map[j][1]](1);
     252             :         }
     253       25044 :         else if (_subch_type[j] == EChannelType::CENTER &&
     254       15108 :                  ((_chan_to_pin_map[i][0] == _chan_to_pin_map[j][1] &&
     255         516 :                    _chan_to_pin_map[i][1] == _chan_to_pin_map[j][2]) ||
     256       15108 :                   (_chan_to_pin_map[i][0] == _chan_to_pin_map[j][2] &&
     257         276 :                    _chan_to_pin_map[i][1] == _chan_to_pin_map[j][1])))
     258             :         {
     259           0 :           x0 = _pin_position[_chan_to_pin_map[j][0]](0);
     260           0 :           y0 = _pin_position[_chan_to_pin_map[j][0]](1);
     261             :         }
     262       25560 :         x1 = 0.5 *
     263       25560 :              (_pin_position[_chan_to_pin_map[i][0]](0) + _pin_position[_chan_to_pin_map[i][1]](0));
     264       25560 :         y1 = 0.5 *
     265       25560 :              (_pin_position[_chan_to_pin_map[i][0]](1) + _pin_position[_chan_to_pin_map[i][1]](1));
     266       25560 :         a1 = _pin_diameter / 2.0 + _duct_to_pin_gap / 2.0;
     267       25560 :         a2 = std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)) + a1;
     268       25560 :         _subchannel_position[i][0] = (a2 * x1 - a1 * x0) / (a2 - a1);
     269       25560 :         _subchannel_position[i][1] = (a2 * y1 - a1 * y0) / (a2 - a1);
     270             :       } // j
     271             :     }
     272         240 :     else if (_subch_type[i] == EChannelType::CORNER)
     273             :     {
     274         240 :       x0 = _pin_position[0](0);
     275         240 :       y0 = _pin_position[0](1);
     276         240 :       x1 = _pin_position[_chan_to_pin_map[i][0]](0);
     277         240 :       y1 = _pin_position[_chan_to_pin_map[i][0]](1);
     278         240 :       a1 = _pin_diameter / 2.0 + _duct_to_pin_gap / 2.0;
     279         240 :       a2 = std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)) + a1;
     280         240 :       _subchannel_position[i][0] = (a2 * x1 - a1 * x0) / (a2 - a1);
     281         240 :       _subchannel_position[i][1] = (a2 * y1 - a1 * y0) / (a2 - a1);
     282             :     }
     283             : 
     284             :     /// Special case _n_rings == 1
     285        1896 :     if (_n_rings == 1)
     286             :     {
     287           0 :       for (unsigned int i = 0; i < _n_channels; i++)
     288             :       {
     289           0 :         Real angle = (2 * i + 1) * libMesh::pi / 6.0;
     290           0 :         _subch_type[i] = EChannelType::CORNER;
     291           0 :         _subchannel_position[i][0] = std::cos(angle) * _flat_to_flat / 2.0;
     292           0 :         _subchannel_position[i][1] = std::sin(angle) * _flat_to_flat / 2.0;
     293             :       }
     294             :     }
     295             :   }
     296          40 : }
     297             : 
     298             : std::unique_ptr<MeshBase>
     299          40 : SCMDetailedTriSubChannelMeshGenerator::generate()
     300             : {
     301          40 :   auto mesh_base = buildMeshBaseObject();
     302             :   BoundaryInfo & boundary_info = mesh_base->get_boundary_info();
     303          40 :   mesh_base->set_spatial_dimension(3);
     304             :   // Define the resolution (the number of points used to represent a circle).
     305             :   // This must be divisible by 4.
     306             :   const unsigned int theta_res_triangle = 24; // TODO: parameterize
     307             :   const unsigned int theta_res_square = 24;   // TODO: parameterize
     308             :   // Compute the number of points needed to represent one sixth and quadrant of a circle.
     309             :   const unsigned int points_per_sixth = theta_res_triangle / 6 + 1;
     310             :   const unsigned int points_per_quadrant = theta_res_square / 4 + 1;
     311             : 
     312             :   // Compute the points needed to represent one axial cross-flow of a subchannel.
     313             :   // For the center subchannel (sc) there is one center point plus the points from 3 side
     314             :   // circles.
     315          40 :   const unsigned int points_per_center = points_per_sixth * 3 + 1;
     316             :   // For the corner sc there is one center point plus the points from 1 side circle plus 3
     317             :   // corners
     318          40 :   const unsigned int points_per_corner = points_per_sixth * 1 + 1 + 3;
     319             :   // For the side sc there is one center point plus the points from 2 intersecting circles plus 2
     320             :   // corners
     321          40 :   const unsigned int points_per_side = points_per_quadrant * 2 + 1 + 2;
     322             : 
     323          40 :   if (_verbose)
     324             :   {
     325           7 :     _console << "Points per center: " << points_per_center << std::endl;
     326           7 :     _console << "Points per side: " << points_per_side << std::endl;
     327           7 :     _console << "Points per corner: " << points_per_corner << std::endl;
     328             :   }
     329             : 
     330             :   // Compute the number of elements (Prism6) which combined base creates the sub-channel
     331             :   // cross-section
     332          40 :   const unsigned int elems_per_center = theta_res_triangle * 3 / 6 + 3; // TODO: check
     333          40 :   const unsigned int elems_per_corner = theta_res_triangle / 6 + 4;
     334          40 :   const unsigned int elems_per_side = 2 * theta_res_square / 4 + 4;
     335          40 :   if (_verbose)
     336             :   {
     337           7 :     _console << "Elems per center: " << elems_per_center << std::endl;
     338           7 :     _console << "Elems per side: " << elems_per_side << std::endl;
     339           7 :     _console << "Elems per corner: " << elems_per_corner << std::endl;
     340             :   }
     341             : 
     342             :   // specify number and type of sub-channel
     343             :   unsigned int n_center, n_side, n_corner;
     344          40 :   if (_n_rings == 1)
     345             :   {
     346           0 :     n_corner = 6;
     347           0 :     n_side = 0;
     348           0 :     n_center = _n_channels - n_side - n_corner;
     349             :   }
     350             :   else
     351             :   {
     352          40 :     n_corner = 6;
     353          40 :     n_side = (_n_rings - 1) * 6;
     354          40 :     n_center = _n_channels - n_side - n_corner;
     355             :   }
     356          40 :   if (_verbose)
     357             :   {
     358           7 :     _console << "Centers: " << n_center << std::endl;
     359           7 :     _console << "Sides: " << n_side << std::endl;
     360           7 :     _console << "Corners: " << n_corner << std::endl;
     361             :   }
     362             : 
     363             :   // Compute the total number of points and elements.
     364          40 :   const unsigned int points_per_level =
     365          40 :       n_corner * points_per_corner + n_side * points_per_side + n_center * points_per_center;
     366          40 :   const unsigned int elems_per_level =
     367          40 :       n_corner * elems_per_corner + n_side * elems_per_side + n_center * elems_per_center;
     368          40 :   if (_verbose)
     369             :   {
     370           7 :     _console << "Points per level: " << points_per_level << std::endl;
     371           7 :     _console << "Elements per level: " << elems_per_level << std::endl;
     372             :   }
     373          40 :   const unsigned int n_points = points_per_level * (_n_cells + 1);
     374          40 :   const unsigned int n_elems = elems_per_level * _n_cells;
     375          40 :   if (_verbose)
     376             :   {
     377           7 :     _console << "Number of points: " << n_points << std::endl;
     378           7 :     _console << "Number of elements: " << n_elems << std::endl;
     379             :   }
     380          40 :   mesh_base->reserve_nodes(n_points);
     381          40 :   mesh_base->reserve_elem(n_elems);
     382             :   // Build an array of points arranged in a circle on the xy-plane. (last and first node overlap)
     383             :   // We build for both the square discretization in the edges and the triangular discretization
     384             :   // within the mesh
     385          40 :   const Real radius = _pin_diameter / 2.0;
     386             :   std::array<Point, theta_res_square + 1> circle_points_square;
     387             :   {
     388             :     Real theta = 0;
     389        1040 :     for (unsigned int i = 0; i < theta_res_square + 1; i++)
     390             :     {
     391        1000 :       circle_points_square[i](0) = radius * std::cos(theta);
     392        1000 :       circle_points_square[i](1) = radius * std::sin(theta);
     393        1000 :       theta += 2.0 * libMesh::pi / theta_res_square;
     394             :     }
     395             :   }
     396             :   std::array<Point, theta_res_triangle + 1> circle_points_triangle;
     397             :   {
     398             :     Real theta = 0;
     399        1040 :     for (unsigned int i = 0; i < theta_res_triangle + 1; i++)
     400             :     {
     401        1000 :       circle_points_triangle[i](0) = radius * std::cos(theta);
     402        1000 :       circle_points_triangle[i](1) = radius * std::sin(theta);
     403        1000 :       theta += 2.0 * libMesh::pi / theta_res_triangle;
     404             :     }
     405             :   }
     406             :   // Define "quadrant center" reference points.  These will be the centers of
     407             :   // the 3 circles that represent the fuel pins.  These centers are
     408             :   // offset a little bit so that in the final mesh, there is a tiny gap between
     409             :   // neighboring subchannel cells.  That allows us to easily map a solution to
     410             :   // this detailed mesh with a nearest-neighbor search.
     411             :   const Real shrink_factor = 0.99999;
     412             :   // Quadrants are used only for the side and corner subchannels
     413          40 :   Real _duct_to_pin_gap =
     414          40 :       0.5 * (_flat_to_flat - (_n_rings - 1) * _pitch * std::sqrt(3.0) - _pin_diameter);
     415             :   std::array<Point, 2> quadrant_centers_sides;
     416          40 :   quadrant_centers_sides[0] = Point(
     417          40 :       -_pitch * 0.5 * shrink_factor, -(_duct_to_pin_gap + _pin_diameter) * 0.5 * shrink_factor, 0);
     418          40 :   quadrant_centers_sides[1] = Point(
     419             :       _pitch * 0.5 * shrink_factor, -(_duct_to_pin_gap + _pin_diameter) * 0.5 * shrink_factor, 0);
     420             :   std::array<Point, 1> quadrant_centers_corner;
     421          40 :   quadrant_centers_corner[0] =
     422          40 :       Point(-(_duct_to_pin_gap + _pin_diameter) * 0.5 * std::sin(libMesh::pi / 6) * shrink_factor,
     423          40 :             -(_duct_to_pin_gap + _pin_diameter) * 0.5 * std::cos(libMesh::pi / 6) * shrink_factor,
     424             :             0);
     425             :   // Triangles are used for all center subchannels
     426             :   std::array<Point, 3> triangle_centers;
     427          40 :   triangle_centers[0] = Point(0, _pitch * std::cos(libMesh::pi / 6) * 2 / 3 * shrink_factor, 0);
     428          40 :   triangle_centers[1] = Point(-_pitch * 0.5 * shrink_factor,
     429          40 :                               -_pitch * std::cos(libMesh::pi / 6) * 1 / 3 * shrink_factor,
     430             :                               0);
     431          40 :   triangle_centers[2] = Point(
     432             :       _pitch * 0.5 * shrink_factor, -_pitch * std::cos(libMesh::pi / 6) * 1 / 3 * shrink_factor, 0);
     433             : 
     434             :   const unsigned int m_sixth = theta_res_triangle / 6;
     435             :   const unsigned int m_quarter = theta_res_square / 4;
     436             :   // Build an array of points that represent a cross section of a center subchannel
     437             :   // cell.  The points are ordered in this fashion:
     438             :   //    3   1
     439             :   //      2
     440             :   //      0
     441             :   // 4 5         8 9
     442             :   //  6         7
     443             :   std::array<Point, points_per_center> center_points;
     444             :   {
     445             :     unsigned int start;
     446         160 :     for (unsigned int i = 0; i < 3; i++)
     447             :     {
     448         120 :       if (i == 0)
     449             :         start = 5 * (m_sixth);
     450          80 :       if (i == 1)
     451             :         start = 1 * (m_sixth);
     452          80 :       if (i == 2)
     453             :         start = 3 * (m_sixth);
     454         720 :       for (unsigned int ii = 0; ii < points_per_sixth; ii++)
     455             :       {
     456         600 :         auto c_pt = circle_points_triangle[start - ii];
     457         600 :         center_points[i * points_per_sixth + ii + 1] = triangle_centers[i] + c_pt;
     458             :       }
     459             :     }
     460             :   }
     461             : 
     462             :   // Build an array of points that represent a cross section of a top left corner subchannel
     463             :   // cell. The points are ordered in this fashion (x direction towards point 4, y direction towards
     464             :   // point 6):
     465             :   //    5       6
     466             :   //
     467             :   //      0
     468             :   // 4        2 3
     469             :   //        1
     470             :   std::array<Point, points_per_corner> corner_points;
     471             :   {
     472         240 :     for (unsigned int ii = 0; ii < points_per_sixth; ii++)
     473             :     {
     474         200 :       auto c_pt = circle_points_triangle[1 * m_quarter - ii];
     475         200 :       corner_points[ii + 1] = quadrant_centers_corner[0] + c_pt;
     476             :     }
     477             :     Real side_short = (_duct_to_pin_gap + _pin_diameter) * 0.5;
     478          40 :     Real side_long = (2.0 * _duct_to_pin_gap + _pin_diameter) * 0.5;
     479          40 :     Real side_length = std::sqrt(std::pow(side_short, 2) + std::pow(side_long, 2) -
     480          40 :                                  2 * side_short * side_long * std::cos(libMesh::pi / 6));
     481             :     Real angle =
     482             :         libMesh::pi - libMesh::pi / 3 -
     483          40 :         std::acos((-std::pow(side_long, 2) + std::pow(side_short, 2) + std::pow(side_length, 2)) /
     484          40 :                   (2 * side_short * side_length));
     485          40 :     corner_points[points_per_sixth + 1] = Point(side_length * std::cos(angle) * shrink_factor,
     486          40 :                                                 -side_length * std::sin(angle) * shrink_factor,
     487             :                                                 0);
     488          40 :     corner_points[points_per_sixth + 2] =
     489          40 :         Point(side_length * std::sin(libMesh::pi / 2 - angle - libMesh::pi / 6) * shrink_factor *
     490             :                   std::tan(libMesh::pi / 6),
     491          40 :               side_length * std::sin(libMesh::pi / 2 - angle - libMesh::pi / 6) * shrink_factor,
     492             :               0);
     493          40 :     corner_points[points_per_sixth + 3] =
     494          40 :         Point(-side_length * std::cos(libMesh::pi / 2 - angle - libMesh::pi / 6) * shrink_factor,
     495             :               side_length * std::sin(libMesh::pi / 2 - angle - libMesh::pi / 6) * shrink_factor,
     496             :               0);
     497             :   }
     498             : 
     499             :   // Build an array of points that represent a cross-section of a top side subchannel
     500             :   // cell.  The points are ordered in this fashion:
     501             :   // 8            7
     502             :   //
     503             :   //       0
     504             :   // 1 2        5 6
     505             :   //    3     4
     506             :   std::array<Point, points_per_side> side_points;
     507             :   {
     508         320 :     for (unsigned int ii = 0; ii < points_per_quadrant; ii++)
     509             :     {
     510         280 :       auto c_pt = circle_points_square[m_quarter - ii];
     511         280 :       side_points[ii + 1] = quadrant_centers_sides[0] + c_pt;
     512             :     }
     513         320 :     for (unsigned int ii = 0; ii < points_per_quadrant; ii++)
     514             :     {
     515         280 :       auto c_pt = circle_points_square[2 * m_quarter - ii];
     516         280 :       side_points[points_per_quadrant + ii + 1] = quadrant_centers_sides[1] + c_pt;
     517             :     }
     518          40 :     side_points[2 * points_per_quadrant + 1] =
     519          40 :         Point(_pitch * 0.5 * shrink_factor, 0.5 * _duct_to_pin_gap * shrink_factor, 0);
     520          40 :     side_points[2 * points_per_quadrant + 2] =
     521             :         Point(-_pitch * 0.5 * shrink_factor, 0.5 * _duct_to_pin_gap * shrink_factor, 0);
     522             :   }
     523             : 
     524          40 :   int point_counter = 0;
     525             :   unsigned int node_id = 0;
     526        1936 :   for (unsigned int i = 0; i < _n_channels; i++)
     527             :   {
     528             :     //    Real offset_x = _flat_to_flat / 2.0;
     529             :     //    Real offset_y = _flat_to_flat / 2.0;
     530             : 
     531        1896 :     if (getSubchannelType(i) == EChannelType::CENTER)
     532             :     {
     533       38040 :       for (auto z : _z_grid)
     534             :       {
     535             :         // Get height
     536             :         Point z0{0, 0, z};
     537             : 
     538             :         // Get suchannel position and assign to point
     539       36900 :         auto loc_position = getSubchannelPosition(i);
     540       36900 :         Point p0{loc_position[0], loc_position[1], 0};
     541             : 
     542             :         // Determine orientation of current subchannel
     543       36900 :         auto subchannel_pins = getSubChannelPins(i);
     544             :         Point subchannel_side =
     545       36900 :             getPinPosition(subchannel_pins[0]) + getPinPosition(subchannel_pins[1]);
     546             :         Point base_center_orientation = {0, -1};
     547             : 
     548             :         // Get rotation angle for current subchannel
     549             :         Real dot_prod = 0;
     550      110700 :         for (unsigned int lp = 0; lp < 2; lp++)
     551       73800 :           dot_prod += base_center_orientation(lp) * subchannel_side(lp);
     552             :         auto theta =
     553       36900 :             std::acos(dot_prod / (base_center_orientation.norm() * subchannel_side.norm()));
     554       36900 :         if (subchannel_side(0) < 0)
     555       18450 :           theta = 2.0 * libMesh::pi - theta;
     556             : 
     557             :         //        Real distance_side = subchannel_side.norm();
     558             :         //        Real distance_top = getPinPosition(subchannel_pins[2]).norm();
     559             :         //        if (distance_top > distance_side)
     560             :         //                  theta += libMesh::pi * 0.0;
     561             : 
     562       36900 :         theta += _orientation_map[i];
     563             : 
     564       36900 :         theta = trunc((theta + (libMesh::pi / 6.0)) / (libMesh::pi / 3.0)) * libMesh::pi / 3.0;
     565             : 
     566       36900 :         if (_verbose)
     567             :         {
     568        3528 :           if (z == 0)
     569             :           {
     570         168 :             _console << "Subchannel Position: " << p0 << std::endl;
     571         168 :             auto pins = getSubChannelPins(i);
     572         672 :             for (auto r : pins)
     573         504 :               _console << r << " ";
     574         168 :             _console << std::endl;
     575         168 :             _console << "Theta: " << theta / libMesh::pi * 180. << std::endl;
     576         168 :           }
     577             :         }
     578             : 
     579             :         // Assigning points for center channels
     580      627300 :         for (unsigned int i = 0; i < points_per_center; i++)
     581             :         {
     582      590400 :           auto new_point = rotatePoint(center_points[i], theta) + p0 + z0;
     583      590400 :           if (_verbose)
     584             :           {
     585       56448 :             if (z == 0)
     586        2688 :               _console << i << " - " << new_point << std::endl;
     587             :           }
     588      590400 :           mesh_base->add_point(new_point, node_id++);
     589      590400 :           point_counter += 1;
     590             :         }
     591       36900 :       }
     592             :     }
     593         756 :     else if (getSubchannelType(i) == EChannelType::EDGE)
     594             :     {
     595       17832 :       for (auto z : _z_grid)
     596             :       {
     597             :         // Get height
     598             :         Point z0{0, 0, z};
     599             : 
     600             :         // Get suchannel position and assign to point
     601       17316 :         auto loc_position = getSubchannelPosition(i);
     602       17316 :         Point p0{loc_position[0], loc_position[1], 0};
     603             : 
     604             :         // Determine orientation of current subchannel
     605       17316 :         auto subchannel_pins = getSubChannelPins(i);
     606             :         Point subchannel_side =
     607       17316 :             getPinPosition(subchannel_pins[0]) + getPinPosition(subchannel_pins[1]);
     608             :         Point base_center_orientation = {0, 1};
     609             : 
     610             :         // Get rotation angle for current subchannel
     611             :         Real dot_prod = 0;
     612       51948 :         for (unsigned int lp = 0; lp < 2; lp++)
     613       34632 :           dot_prod += base_center_orientation(lp) * subchannel_side(lp);
     614             :         auto theta =
     615       17316 :             std::acos(dot_prod / (base_center_orientation.norm() * subchannel_side.norm()));
     616       17316 :         if (subchannel_side(0) > 0)
     617        8658 :           theta = 2. * libMesh::pi - theta;
     618       17316 :         theta = trunc((theta + (libMesh::pi / 6.0)) / (libMesh::pi / 3.0)) * libMesh::pi / 3.0;
     619             : 
     620       17316 :         if (_verbose)
     621             :         {
     622        1764 :           if (z == 0)
     623             :           {
     624          84 :             _console << "Subchannel Position: " << p0 << std::endl;
     625          84 :             auto pins = getSubChannelPins(i);
     626         252 :             for (auto r : pins)
     627         168 :               _console << r << " ";
     628          84 :             _console << std::endl;
     629          84 :             _console << "Theta: " << theta * 180 / libMesh::pi << std::endl;
     630          84 :           }
     631             :         }
     632             : 
     633             :         // Assigning points for center channels
     634      311688 :         for (unsigned int i = 0; i < points_per_side; i++)
     635             :         {
     636      294372 :           auto new_point = rotatePoint(side_points[i], theta) + p0 + z0;
     637      294372 :           if (_verbose)
     638             :           {
     639       29988 :             if (z == 0)
     640        1428 :               _console << i << " - " << new_point << std::endl;
     641             :           }
     642      294372 :           mesh_base->add_point(new_point, node_id++);
     643      294372 :           point_counter += 1;
     644             :         }
     645       17316 :       }
     646             :     }
     647             :     else // getSubchannelType(i) == EChannelType::CORNER
     648             :     {
     649        8520 :       for (auto z : _z_grid)
     650             :       {
     651             :         // Get height
     652             :         Point z0{0, 0, z};
     653             : 
     654             :         // Get suchannel position and assign to point
     655        8280 :         auto loc_position = getSubchannelPosition(i);
     656        8280 :         Point p0{loc_position[0], loc_position[1], 0};
     657             : 
     658             :         // Determine orientation of current subchannel
     659        8280 :         auto subchannel_pins = getSubChannelPins(i);
     660        8280 :         Point subchannel_side = getPinPosition(subchannel_pins[0]);
     661             :         Point base_center_orientation = {1, 1};
     662             : 
     663             :         // Get rotation angle for current subchannel
     664             :         Real dot_prod = 0;
     665       24840 :         for (unsigned int lp = 0; lp < 2; lp++)
     666       16560 :           dot_prod += base_center_orientation(lp) * subchannel_side(lp);
     667             :         auto theta =
     668        8280 :             std::acos(dot_prod / (base_center_orientation.norm() * subchannel_side.norm()));
     669        8280 :         if (subchannel_side(0) > 0)
     670        4140 :           theta = 2. * libMesh::pi - theta;
     671        8280 :         theta = trunc((theta + (libMesh::pi / 6.0)) / (libMesh::pi / 3.0)) * libMesh::pi / 3.0;
     672             : 
     673        8280 :         if (_verbose)
     674             :         {
     675         882 :           if (z == 0)
     676             :           {
     677          42 :             _console << "Subchannel Position: " << p0 << std::endl;
     678          42 :             auto pins = getSubChannelPins(i);
     679          84 :             for (auto r : pins)
     680          42 :               _console << r << " ";
     681          42 :             _console << std::endl;
     682          42 :             _console << "Theta: " << theta * 180 / libMesh::pi << std::endl;
     683          42 :           }
     684             :         }
     685             : 
     686             :         // Assigning points for center channels
     687       82800 :         for (unsigned int i = 0; i < points_per_corner; i++)
     688             :         {
     689       74520 :           auto new_point = rotatePoint(corner_points[i], theta) + p0 + z0;
     690       74520 :           if (_verbose)
     691             :           {
     692        7938 :             if (z == 0)
     693         378 :               _console << i << " - " << new_point << std::endl;
     694             :           }
     695       74520 :           mesh_base->add_point(new_point, node_id++);
     696       74520 :           point_counter += 1;
     697             :         }
     698        8280 :       }
     699             :     }
     700             :   } // i
     701          40 :   if (_verbose)
     702           7 :     _console << "Point counter: " << point_counter << std::endl;
     703             : 
     704          40 :   int element_counter = 0;
     705             :   unsigned int elem_id = 0;
     706             :   unsigned int number_of_corner = 0;
     707             :   unsigned int number_of_side = 0;
     708             :   unsigned int number_of_center = 0;
     709             :   unsigned int elems_per_channel = 0;
     710             :   unsigned int points_per_channel = 0;
     711        1936 :   for (unsigned int i = 0; i < _n_channels; i++)
     712             :   {
     713             :     auto subch_type = getSubchannelType(i);
     714        1896 :     if (subch_type == EChannelType::CORNER)
     715             :     {
     716         240 :       number_of_corner++;
     717             :       elems_per_channel = elems_per_corner;
     718             :       points_per_channel = points_per_corner;
     719         240 :       if (_verbose)
     720          42 :         _console << "Corner" << std::endl;
     721             :     }
     722        1656 :     else if (subch_type == EChannelType::EDGE)
     723             :     {
     724         516 :       number_of_side++;
     725             :       elems_per_channel = elems_per_side;
     726             :       points_per_channel = points_per_side;
     727         516 :       if (_verbose)
     728          84 :         _console << "Edge" << std::endl;
     729             :     }
     730        1140 :     else if (subch_type == EChannelType::CENTER)
     731             :     {
     732        1140 :       number_of_center++;
     733             :       elems_per_channel = elems_per_center;
     734             :       points_per_channel = points_per_center;
     735        1140 :       if (_verbose)
     736         168 :         _console << "Center" << std::endl;
     737             :     }
     738       62496 :     for (unsigned int iz = 0; iz < _n_cells; iz++)
     739             :     {
     740       60600 :       unsigned int elapsed_points = number_of_corner * points_per_corner * (_n_cells + 1) +
     741       60600 :                                     number_of_side * points_per_side * (_n_cells + 1) +
     742       60600 :                                     number_of_center * points_per_center * (_n_cells + 1) -
     743       60600 :                                     points_per_channel * (_n_cells + 1);
     744             :       // index of the central node at base of cell
     745       60600 :       unsigned int indx1 = iz * points_per_channel + elapsed_points;
     746             :       // index of the central node at top of cell
     747       60600 :       unsigned int indx2 = (iz + 1) * points_per_channel + elapsed_points;
     748             : 
     749      930120 :       for (unsigned int i = 0; i < elems_per_channel; i++)
     750             :       {
     751      869520 :         Elem * elem = new Prism6;
     752      869520 :         elem->subdomain_id() = _block_id;
     753      869520 :         elem->set_id(elem_id++);
     754      869520 :         elem = mesh_base->add_elem(elem);
     755             : 
     756      869520 :         if (_verbose)
     757       84000 :           _console << "Node 0: " << *mesh_base->node_ptr(indx1) << std::endl;
     758             : 
     759      869520 :         elem->set_node(0, mesh_base->node_ptr(indx1));
     760      869520 :         elem->set_node(1, mesh_base->node_ptr(indx1 + i + 1));
     761      869520 :         if (i != elems_per_channel - 1)
     762      808920 :           elem->set_node(2, mesh_base->node_ptr(indx1 + i + 2));
     763             :         else
     764       60600 :           elem->set_node(2, mesh_base->node_ptr(indx1 + 1));
     765             : 
     766      869520 :         elem->set_node(3, mesh_base->node_ptr(indx2));
     767      869520 :         elem->set_node(4, mesh_base->node_ptr(indx2 + i + 1));
     768      869520 :         if (i != elems_per_channel - 1)
     769      808920 :           elem->set_node(5, mesh_base->node_ptr(indx2 + i + 2));
     770             :         else
     771       60600 :           elem->set_node(5, mesh_base->node_ptr(indx2 + 1));
     772             : 
     773      869520 :         if (iz == 0)
     774       27276 :           boundary_info.add_side(elem, 0, 0);
     775      869520 :         if (iz == _n_cells - 1)
     776       27276 :           boundary_info.add_side(elem, 4, 1);
     777             : 
     778      869520 :         element_counter += 1;
     779             :       }
     780             :     }
     781             :   }
     782          40 :   if (_verbose)
     783           7 :     _console << "Element counter: " << element_counter << std::endl;
     784          40 :   boundary_info.sideset_name(0) = "inlet";
     785          40 :   boundary_info.sideset_name(1) = "outlet";
     786          40 :   mesh_base->subdomain_name(_block_id) = name();
     787          40 :   if (_verbose)
     788           7 :     _console << "Mesh assembly done" << std::endl;
     789          40 :   mesh_base->prepare_for_use();
     790             : 
     791          40 :   return mesh_base;
     792           0 : }
     793             : 
     794             : Point
     795      959292 : SCMDetailedTriSubChannelMeshGenerator::rotatePoint(Point b, Real theta)
     796             : {
     797             : 
     798             :   // Building rotation matrix
     799             :   std::vector<std::vector<Real>> A;
     800      959292 :   A.resize(3);
     801     3837168 :   for (std::vector<Real> a : A)
     802             :   {
     803     2877876 :     a.resize(3);
     804     2877876 :   }
     805             : 
     806      959292 :   A[0] = {std::cos(theta), -std::sin(theta), 0.0};
     807      959292 :   A[1] = {std::sin(theta), std::cos(theta), 0.0};
     808      959292 :   A[2] = {0.0, 0.0, 1.0};
     809             : 
     810             :   // Rotating vector
     811             :   Point rotated_vector = Point(0.0, 0.0, 0.0);
     812     3837168 :   for (unsigned int i = 0; i < 3; i++)
     813             :   {
     814    11511504 :     for (unsigned int j = 0; j < 3; j++)
     815             :     {
     816     8633628 :       rotated_vector(i) += A[i][j] * b(j);
     817             :     }
     818             :   }
     819             : 
     820      959292 :   return rotated_vector;
     821      959292 : }
     822             : 
     823             : Point
     824           0 : SCMDetailedTriSubChannelMeshGenerator::translatePoint(Point & b, Point & translation_vector)
     825             : {
     826             :   // Translating point
     827             :   Point translated_vector = Point(0.0, 0.0, 0.0);
     828           0 :   for (unsigned int i = 0; i < 3; i++)
     829             :   {
     830           0 :     translated_vector(i) = b(i) + translation_vector(i);
     831             :   }
     832             : 
     833           0 :   return translated_vector;
     834             : }

Generated by: LCOV version 1.14