LCOV - code coverage report
Current view: top level - src/meshgenerators - SCMDetailedTriAssemblyMeshGenerator.C (source / functions) Hit Total Coverage
Test: idaholab/moose subchannel: #33187 (5aa0b2) with base d7c4bd Lines: 440 450 97.8 %
Date: 2026-06-30 12:24:57 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14