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

Generated by: LCOV version 1.14