LCOV - code coverage report
Current view: top level - src/userobjects - ViewFactorRayStudy.C (source / functions) Hit Total Coverage
Test: idaholab/moose heat_transfer: #31405 (292dce) with base fef103 Lines: 219 231 94.8 %
Date: 2025-09-04 07:53:51 Functions: 15 15 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 "ViewFactorRayStudy.h"
      11             : 
      12             : // Local includes
      13             : #include "ViewFactorRayBC.h"
      14             : #include "GeometryUtils.h"
      15             : 
      16             : // libMesh includes
      17             : #include "libmesh/parallel_algebra.h"
      18             : #include "libmesh/parallel_sync.h"
      19             : #include "libmesh/enum_quadrature_type.h"
      20             : #include "libmesh/fe_base.h"
      21             : #include "libmesh/quadrature.h"
      22             : 
      23             : // Ray tracing includes
      24             : #include "ReflectRayBC.h"
      25             : #include "RayTracingPackingUtils.h"
      26             : 
      27             : using namespace libMesh;
      28             : 
      29             : registerMooseObject("HeatTransferApp", ViewFactorRayStudy);
      30             : 
      31             : InputParameters
      32         330 : ViewFactorRayStudy::validParams()
      33             : {
      34         330 :   auto params = RayTracingStudy::validParams();
      35             : 
      36         660 :   params.addRequiredParam<std::vector<BoundaryName>>(
      37             :       "boundary", "The list of boundaries where view factors are desired");
      38             : 
      39             :   MooseEnum qorders("CONSTANT FIRST SECOND THIRD FOURTH FIFTH SIXTH SEVENTH EIGHTH NINTH TENTH "
      40             :                     "ELEVENTH TWELFTH THIRTEENTH FOURTEENTH FIFTEENTH SIXTEENTH SEVENTEENTH "
      41             :                     "EIGHTTEENTH NINTEENTH TWENTIETH",
      42         660 :                     "CONSTANT");
      43         660 :   params.addParam<MooseEnum>("face_order", qorders, "The face quadrature rule order");
      44             : 
      45         660 :   MooseEnum qtypes("GAUSS GRID", "GRID");
      46         660 :   params.addParam<MooseEnum>("face_type", qtypes, "The face quadrature type");
      47             : 
      48         660 :   MooseEnum convention("positive=0 negative=1", "positive");
      49         660 :   params.addParam<MooseEnum>(
      50             :       "internal_convention",
      51             :       convention,
      52             :       "The convention for spawning rays from internal sidesets; denotes the sign of the dot "
      53             :       "product between a ray and the internal sideset side normal");
      54             : 
      55         660 :   params.addParam<unsigned int>(
      56             :       "polar_quad_order",
      57         660 :       16,
      58             :       "Order of the polar quadrature [polar angle is between ray and normal]. Must be even.");
      59         660 :   params.addParam<unsigned int>(
      60             :       "azimuthal_quad_order",
      61         660 :       8,
      62             :       "Order of the azimuthal quadrature per quadrant [azimuthal angle is measured in "
      63             :       "a plane perpendicular to the normal].");
      64             : 
      65             :   // Shouldn't ever need RayKernels for view factors
      66         330 :   params.set<bool>("ray_kernel_coverage_check") = false;
      67         330 :   params.suppressParameter<bool>("ray_kernel_coverage_check");
      68             : 
      69             :   // So that the study executes before the RayTracingViewFactor
      70         330 :   params.set<bool>("force_preaux") = true;
      71         330 :   params.suppressParameter<bool>("force_preaux");
      72             : 
      73             :   // Need to use internal sidesets
      74         330 :   params.set<bool>("use_internal_sidesets") = true;
      75         330 :   params.suppressParameter<bool>("use_internal_sidesets");
      76             : 
      77             :   // Don't verify Rays in opt mode by default - it's expensive
      78         330 :   params.set<bool>("verify_rays") = false;
      79             : 
      80             :   // No need to use Ray registration
      81         330 :   params.set<bool>("_use_ray_registration") = false;
      82             :   // Do not need to bank Rays on completion
      83         330 :   params.set<bool>("_bank_rays_on_completion") = false;
      84             : 
      85         330 :   params.addClassDescription(
      86             :       "This ray study is used to compute view factors in cavities with obstruction. It sends out "
      87             :       "rays from surfaces bounding the radiation cavity into a set of directions determined by an "
      88             :       "angular quadrature. The rays are tracked and view factors are computed by determining the "
      89             :       "surface where the ray dies.");
      90         330 :   return params;
      91         330 : }
      92             : 
      93         165 : ViewFactorRayStudy::ViewFactorRayStudy(const InputParameters & parameters)
      94             :   : RayTracingStudy(parameters),
      95         165 :     _bnd_ids_vec(_mesh.getBoundaryIDs(getParam<std::vector<BoundaryName>>("boundary"))),
      96         165 :     _bnd_ids(_bnd_ids_vec.begin(), _bnd_ids_vec.end()),
      97         330 :     _internal_convention(getParam<MooseEnum>("internal_convention")),
      98         165 :     _ray_index_start_bnd_id(registerRayAuxData("start_bnd_id")),
      99         165 :     _ray_index_start_total_weight(registerRayAuxData("start_total_weight")),
     100         165 :     _fe_face(FEBase::build(_mesh.dimension(), FEType(CONSTANT, MONOMIAL))),
     101         660 :     _q_face(QBase::build(Moose::stringToEnum<QuadratureType>(getParam<MooseEnum>("face_type")),
     102         165 :                          _mesh.dimension() - 1,
     103         165 :                          Moose::stringToEnum<Order>(getParam<MooseEnum>("face_order")))),
     104         165 :     _is_3d(_mesh.dimension() == 3),
     105         330 :     _threaded_vf_info(libMesh::n_threads())
     106             : {
     107         165 :   _fe_face->attach_quadrature_rule(_q_face.get());
     108             :   _fe_face->get_xyz();
     109             : 
     110             :   // create angular quadrature
     111         165 :   if (!_is_3d)
     112             :   {
     113             :     // In 2D, we integrate over angle theta instead of mu = cos(theta)
     114             :     // The integral over theta is approximated using a Gauss Legendre
     115             :     // quadrature. The integral we need to approximate is given by:
     116             :     //
     117             :     // int_{-pi/2}^{pi/2} cos(theta) d theta
     118             :     //
     119             :     // We get abscissae x and weight w for range of integration
     120             :     // from 0 to 1 and then rescale it to the integration range
     121             :     //
     122             :     std::vector<Real> x;
     123             :     std::vector<Real> w;
     124          63 :     RayTracingAngularQuadrature::gaussLegendre(
     125         126 :         2 * getParam<unsigned int>("polar_quad_order"), x, w);
     126             : 
     127          63 :     _2d_aq_angles.resize(x.size());
     128          63 :     _2d_aq_weights.resize(x.size());
     129        4511 :     for (unsigned int j = 0; j < x.size(); ++j)
     130             :     {
     131        4448 :       _2d_aq_angles[j] = (2 * x[j] - 1) * M_PI / 2;
     132        4448 :       _2d_aq_weights[j] = w[j] * M_PI;
     133             :     }
     134          63 :     _num_dir = _2d_aq_angles.size();
     135          63 :   }
     136             :   else
     137             :   {
     138         204 :     _3d_aq = std::make_unique<RayTracingAngularQuadrature>(
     139         204 :         _mesh.dimension(),
     140             :         getParam<unsigned int>("polar_quad_order"),
     141         102 :         4 * getParam<unsigned int>("azimuthal_quad_order"),
     142         204 :         /* mu_min = */ 0,
     143         204 :         /* mu_max = */ 1);
     144             : 
     145         102 :     _num_dir = _3d_aq->numDirections();
     146             :   }
     147         165 : }
     148             : 
     149             : void
     150         165 : ViewFactorRayStudy::initialSetup()
     151             : {
     152         165 :   RayTracingStudy::initialSetup();
     153             : 
     154             :   // We optimized away RayKernels, so don't allow them
     155         165 :   if (hasRayKernels(/* tid = */ 0))
     156           0 :     mooseError("Not compatible with RayKernels.");
     157             : 
     158             :   // RayBC coverage checks (at least one ViewFactorRayBC and optionally a ReflectRayBC
     159             :   // on ONLY external boundaries).
     160             :   std::vector<RayBoundaryConditionBase *> ray_bcs;
     161         165 :   RayTracingStudy::getRayBCs(ray_bcs, 0);
     162             :   unsigned int vf_bc_count = 0;
     163         360 :   for (RayBoundaryConditionBase * rbc : ray_bcs)
     164             :   {
     165         195 :     auto view_factor_bc = dynamic_cast<ViewFactorRayBC *>(rbc);
     166         195 :     if (view_factor_bc)
     167             :     {
     168         165 :       ++vf_bc_count;
     169             : 
     170         165 :       if (!view_factor_bc->hasBoundary(_bnd_ids))
     171           0 :         mooseError("The boundary restriction of ",
     172             :                    rbc->type(),
     173             :                    " '",
     174             :                    rbc->name(),
     175             :                    "' does not match 'boundary'");
     176             :     }
     177             :     else
     178             :     {
     179          30 :       auto reflect_bc = dynamic_cast<ReflectRayBC *>(rbc);
     180          30 :       if (reflect_bc)
     181             :       {
     182          30 :         if (reflect_bc->hasBoundary(_bnd_ids))
     183           0 :           mooseError("The boundaries applied in ReflectRayBC '",
     184             :                      rbc->name(),
     185             :                      "' cannot include any of the boundaries in ",
     186             :                      type());
     187             : 
     188          71 :         for (const BoundaryID internal_bnd_id : getInternalSidesets())
     189          41 :           if (reflect_bc->hasBoundary(internal_bnd_id))
     190           0 :             mooseError("The ReflectRayBC '",
     191             :                        rbc->name(),
     192             :                        "' is defined on an internal boundary (",
     193             :                        internal_bnd_id,
     194             :                        ").\n\n",
     195             :                        "This is not allowed for view factor computation.");
     196             :       }
     197             :       else
     198           0 :         mooseError("Does not support the ",
     199             :                    rbc->type(),
     200             :                    " ray boundary condition.\nSupported RayBCs: ReflectRayBC and ViewFactorRayBC.");
     201             :     }
     202         195 :     if (vf_bc_count != 1)
     203           0 :       mooseError("Requires one and only one ViewFactorRayBC.");
     204             :   }
     205         165 : }
     206             : 
     207             : void
     208         122 : ViewFactorRayStudy::preExecuteStudy()
     209             : {
     210             :   // Clear and zero the view factor maps we're about to accumulate into for each thread
     211         266 :   for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
     212             :   {
     213         144 :     _threaded_vf_info[tid].clear();
     214        1107 :     for (const BoundaryID from_id : _bnd_ids)
     215        7680 :       for (const BoundaryID to_id : _bnd_ids)
     216        6717 :         _threaded_vf_info[tid][from_id][to_id] = 0;
     217             :   }
     218             : 
     219         122 :   generateStartElems();
     220         122 : }
     221             : 
     222             : void
     223         122 : ViewFactorRayStudy::postExecuteStudy()
     224             : {
     225             :   // Finalize the cumulative _vf_info;
     226             :   _vf_info.clear();
     227         942 :   for (const BoundaryID from_id : _bnd_ids)
     228        6568 :     for (const BoundaryID to_id : _bnd_ids)
     229             :     {
     230        5748 :       Real & entry = _vf_info[from_id][to_id];
     231             : 
     232             :       // Zero before summing
     233        5748 :       entry = 0;
     234             : 
     235             :       // Sum over threads
     236       12465 :       for (THREAD_ID tid = 0; tid < libMesh::n_threads(); ++tid)
     237       13434 :         entry += _threaded_vf_info[tid][from_id][to_id];
     238             : 
     239             :       // Sum over processors
     240        5748 :       _communicator.sum(entry);
     241             :     }
     242         122 : }
     243             : 
     244             : void
     245         122 : ViewFactorRayStudy::generateRays()
     246             : {
     247         244 :   TIME_SECTION("generateRays", 3, "ViewFactorRayStudy Generating Rays");
     248             : 
     249             :   // Determine number of Rays and points to allocate space before generation and for output
     250             :   std::size_t num_local_rays = 0;
     251             :   std::size_t num_local_start_points = 0;
     252        3170 :   for (const auto & start_elem : _start_elems)
     253             :   {
     254        3048 :     num_local_start_points += start_elem._points.size();
     255        3048 :     num_local_rays += start_elem._points.size() * _num_dir;
     256             :   }
     257             : 
     258             :   // Print out totals while we're here
     259         122 :   std::size_t num_total_points = num_local_start_points;
     260         122 :   std::size_t num_total_rays = num_local_rays;
     261         122 :   _communicator.sum(num_total_points);
     262         122 :   _communicator.sum(num_total_rays);
     263         122 :   _console << "ViewFactorRayStudy generated " << num_total_points
     264         122 :            << " points with an angular quadrature of " << _num_dir
     265         122 :            << " directions per point requiring " << num_total_rays << " rays" << std::endl;
     266             : 
     267             :   // Reserve space in the buffer ahead of time before we fill it
     268         122 :   reserveRayBuffer(num_local_rays);
     269             : 
     270             :   Point direction;
     271         122 :   unsigned int num_rays_skipped = 0;
     272             : 
     273             :   // loop through all starting points and spawn rays from each for each point and angle
     274        3170 :   for (const auto & start_elem : _start_elems)
     275             :   {
     276             :     // Get normal for the element we're starting on
     277             :     auto inward_normal =
     278        3048 :         getSideNormal(start_elem._start_elem, start_elem._incoming_side, /* tid = */ 0);
     279             :     // We actually want the normal of the original element (remember that we may swap starting
     280             :     // elements to the element on the other face per requirements of the ray tracer)
     281        3048 :     if (start_elem._start_elem != start_elem._elem)
     282             :       inward_normal *= -1;
     283             :     // Lastly, if the boundary is external and the internal convention is positive, we must
     284             :     // switch the normal because our AQ uses the inward normal
     285        3048 :     if (_internal_convention == 0 &&
     286        3048 :         !start_elem._start_elem->neighbor_ptr(start_elem._incoming_side))
     287             :       inward_normal *= -1;
     288             : 
     289             :     // Rotation for the quadrature to align with the normal; in 3D we can do all of this
     290             :     // once up front using the 3D aq object. For 2D, we will do it within the direction loop
     291        3048 :     if (_is_3d)
     292        2664 :       _3d_aq->rotate(inward_normal);
     293             : 
     294             :     // Loop through all points and then all directions
     295       59208 :     for (std::size_t start_i = 0; start_i < start_elem._points.size(); ++start_i)
     296    21684960 :       for (std::size_t l = 0; l < _num_dir; ++l)
     297             :       {
     298             :         // Get direction of the ray; in 3D we already rotated, in 2D we rotate here
     299    21628800 :         if (_is_3d)
     300    21485952 :           direction = _3d_aq->getDirection(l);
     301             :         else
     302             :         {
     303      142848 :           const Real sin_theta = std::sin(_2d_aq_angles[l]);
     304      142848 :           const Real cos_theta = std::cos(_2d_aq_angles[l]);
     305      142848 :           direction(0) = cos_theta * inward_normal(0) - sin_theta * inward_normal(1);
     306      142848 :           direction(1) = sin_theta * inward_normal(0) + cos_theta * inward_normal(1);
     307      142848 :           direction(2) = 0;
     308             :         }
     309             : 
     310             :         // Angular weight function differs in 2D/3D
     311             :         // 2D: the quadrature abscissae are the angles between direction & normal.
     312             :         //     The integrand is the cosine of that angle
     313             :         // 3D: the quadrature abscissae are the azimuthal angle phi and the cosine of the angle
     314             :         //     between normal and direction (= mu). The integrand is mu in that case.
     315    21628800 :         const auto awf = _is_3d ? inward_normal * direction * _3d_aq->getTotalWeight(l)
     316      142848 :                                 : std::cos(_2d_aq_angles[l]) * _2d_aq_weights[l];
     317    21628800 :         const auto start_weight = start_elem._weights[start_i] * awf;
     318             : 
     319             :         // Skip the ray if it exists the domain through the non-planar side it is starting from.
     320             :         // We do not expect there are any neighbor elements to track it on if it exits the
     321             :         // non-planar side.
     322             :         bool intersection_found = false;
     323    21485952 :         if (_is_3d && start_elem._start_elem &&
     324    43114752 :             !start_elem._start_elem->neighbor_ptr(start_elem._incoming_side) &&
     325    18586368 :             sideIsNonPlanar(start_elem._start_elem, start_elem._incoming_side))
     326             :         {
     327             :           // Find edge on side that is 'in front' of the future ray
     328             :           Point intersection_point(std::numeric_limits<Real>::max(), -1, -1);
     329      129600 :           const auto side_elem = start_elem._start_elem->side_ptr(start_elem._incoming_side);
     330             :           Point proj_dir;
     331      321246 :           for (const auto edge_i : side_elem->side_index_range())
     332             :           {
     333      317898 :             const auto edge_1 = side_elem->side_ptr(edge_i);
     334             :             // Project direction onto (start_point, node 1, node 2)
     335             :             const auto d1 = *edge_1->node_ptr(0) - start_elem._points[start_i];
     336             :             const auto d2 = *edge_1->node_ptr(1) - start_elem._points[start_i];
     337      317898 :             const auto d1_unit = d1.unit();
     338      317898 :             const auto d2_unit = d2.unit();
     339             :             // If the starting point is aligned with the edge, it wont cross it
     340      317898 :             if (MooseUtils::absoluteFuzzyEqual(std::abs(d1_unit * d2_unit), 1))
     341           0 :               continue;
     342      317898 :             const auto normal = (d1_unit.cross(d2_unit)).unit();
     343             : 
     344             :             // One of the nodes must be in front of the start point following the direction
     345      317898 :             if (d1 * direction < 0 && d2 * direction < 0)
     346       65610 :               continue;
     347             : 
     348      252288 :             proj_dir = (direction - (direction * normal) * normal).unit();
     349             : 
     350             :             // Only the side of interest will have the projected direction in between d1 and d2
     351      252288 :             if ((proj_dir * d2_unit > d1_unit * d2_unit) &&
     352             :                 (proj_dir * d1_unit > d1_unit * d2_unit))
     353             :             {
     354      126252 :               const auto dist = geom_utils::distanceFromLine(
     355             :                   start_elem._points[start_i], *edge_1->node_ptr(0), *edge_1->node_ptr(1));
     356             :               // Ortho-normalize the base on the plane
     357             :               intersection_point = start_elem._points[start_i] + dist * proj_dir;
     358             :               intersection_found = true;
     359             :               break;
     360             :             }
     361      317898 :           }
     362             : 
     363             :           // Skip the ray if it goes out of the element
     364      129600 :           const auto grazing_dir = (intersection_point - start_elem._points[start_i]).unit();
     365      129600 :           if (intersection_found && inward_normal * direction < inward_normal * grazing_dir)
     366             :           {
     367        2196 :             num_rays_skipped++;
     368             :             continue;
     369             :           }
     370      129600 :         }
     371             : 
     372             :         // Acquire a Ray and fill with the starting information
     373    21626604 :         std::shared_ptr<Ray> ray = acquireRay();
     374    21626604 :         ray->setStart(
     375    21626604 :             start_elem._points[start_i], start_elem._start_elem, start_elem._incoming_side);
     376    21626604 :         ray->setStartingDirection(direction);
     377    21626604 :         ray->auxData(_ray_index_start_bnd_id) = start_elem._bnd_id;
     378    21626604 :         ray->auxData(_ray_index_start_total_weight) = start_weight;
     379             : 
     380             :         // Move the Ray into the buffer to be traced
     381    21626604 :         moveRayToBuffer(ray);
     382             :       }
     383             :   }
     384         122 :   if (num_rays_skipped)
     385           3 :     mooseInfo(num_rays_skipped,
     386             :               " rays were skipped as they exited the mesh at their starting point through "
     387             :               "non-planar sides.");
     388         122 : }
     389             : 
     390             : void
     391    22971762 : ViewFactorRayStudy::addToViewFactorInfo(Real value,
     392             :                                         const BoundaryID from_id,
     393             :                                         const BoundaryID to_id,
     394             :                                         const THREAD_ID tid)
     395             : {
     396             :   mooseAssert(currentlyPropagating(), "Can only be called during Ray tracing");
     397             :   mooseAssert(_threaded_vf_info[tid].count(from_id),
     398             :               "Threaded view factor info does not have from boundary");
     399             :   mooseAssert(_threaded_vf_info[tid][from_id].count(to_id),
     400             :               "Threaded view factor info does not have from -> to boundary");
     401             : 
     402    22971762 :   _threaded_vf_info[tid][from_id][to_id] += value;
     403    22971762 : }
     404             : 
     405             : Real
     406        5748 : ViewFactorRayStudy::viewFactorInfo(const BoundaryID from_id, const BoundaryID to_id) const
     407             : {
     408             :   auto it = _vf_info.find(from_id);
     409        5748 :   if (it == _vf_info.end())
     410           0 :     mooseError("From boundary id ", from_id, " not in view factor map.");
     411             : 
     412             :   auto itt = it->second.find(to_id);
     413        5748 :   if (itt == it->second.end())
     414           0 :     mooseError("From boundary id ", from_id, " to boundary_id ", to_id, " not in view factor map.");
     415        5748 :   return itt->second;
     416             : }
     417             : 
     418             : void
     419         122 : ViewFactorRayStudy::generateStartElems()
     420             : {
     421             :   const auto & points = _fe_face->get_xyz();
     422             :   const auto & weights = _fe_face->get_JxW();
     423             : 
     424             :   // Clear before filling
     425         122 :   _start_elems.clear();
     426             : 
     427             :   // Starting elements we have that are on the wrong side of an internal boundary
     428             :   std::unordered_map<processor_id_type, std::vector<StartElem>> send_start_map;
     429             : 
     430             :   // Get all possible points on the user defined boundaries on this proc
     431        8719 :   for (const BndElement * belem : *_mesh.getBoundaryElementRange())
     432             :   {
     433        8597 :     const Elem * elem = belem->_elem;
     434        8597 :     const auto side = belem->_side;
     435        8597 :     const auto bnd_id = belem->_bnd_id;
     436             : 
     437             :     // Skip if we don't own you
     438        8597 :     if (elem->processor_id() != _pid)
     439        5549 :       continue;
     440             : 
     441             :     // Skip if the boundary id isn't one we're looking for
     442        3201 :     if (!_bnd_ids.count(bnd_id))
     443        3201 :       continue;
     444             : 
     445             :     // Sanity check on QGRID not working on some types
     446        3048 :     if (_q_face->type() == libMesh::QGRID && elem->type() == TET4)
     447           0 :       mooseError(
     448             :           "Cannot use GRID quadrature type with tetrahedral elements in ViewFactorRayStudy '",
     449           0 :           _name,
     450             :           "'");
     451             : 
     452             :     // The elem/side that we will actually start the trace from
     453             :     // (this may change on internal sidesets)
     454        3048 :     const Elem * start_elem = elem;
     455        3048 :     auto start_side = side;
     456             : 
     457             :     // Reinit this face for points
     458        3048 :     _fe_face->reinit(elem, side);
     459             : 
     460             :     // See if this boundary is internal
     461             :     const Elem * neighbor = elem->neighbor_ptr(side);
     462        3048 :     if (neighbor)
     463             :     {
     464             :       if (!neighbor->active())
     465           0 :         mooseError(type(), " does not work with adaptivity");
     466             : 
     467             :       // With the positive convention, the Rays that we want to spawn from internal boundaries
     468             :       // have positive dot products with the outward normal on the side. The ray-tracer requires
     469             :       // that we provide an element incoming side that is actually incoming (the dot product with
     470             :       // the direction and the normal is negative). Therefore, switch the physical trace to start
     471             :       // from the other element and the corresponding side
     472         663 :       if (_internal_convention == 0)
     473             :       {
     474         663 :         start_elem = neighbor;
     475         663 :         start_side = neighbor->which_neighbor_am_i(elem);
     476             :       }
     477             :     }
     478             : 
     479             :     // If we own the true starting elem, add to our start info. Otherwise, package the
     480             :     // start info to be sent to the processor that will actually start this trace
     481        3048 :     const auto start_pid = start_elem->processor_id();
     482        3048 :     auto & add_to = _pid ? _start_elems : send_start_map[start_pid];
     483        3048 :     add_to.emplace_back(elem, start_elem, start_side, bnd_id, points, weights);
     484             :   }
     485             : 
     486             :   // If the internal convention is positive, we may have points that we switched to another
     487             :   // element for the actual trace, so communicate those to the processors that will
     488             :   // actually be starting them
     489         122 :   if (_internal_convention == 0)
     490             :   {
     491             :     // Functor that takes in StartElems and appends them to our local list
     492          91 :     auto append_start_elems = [this](processor_id_type, const std::vector<StartElem> & start_elems)
     493             :     {
     494          91 :       _start_elems.reserve(_start_elems.size() + start_elems.size());
     495        2463 :       for (const StartElem & start_elem : start_elems)
     496        2372 :         _start_elems.emplace_back(start_elem);
     497         213 :     };
     498             : 
     499             :     // Communicate and act on data
     500         122 :     Parallel::push_parallel_packed_range(_communicator, send_start_map, this, append_start_elems);
     501             :   }
     502         122 : }
     503             : 
     504             : namespace libMesh
     505             : {
     506             : namespace Parallel
     507             : {
     508             : 
     509             : unsigned int
     510          34 : Packing<ViewFactorRayStudy::StartElem>::packing_size(const std::size_t num_points)
     511             : {
     512             :   // Number of points, elem_id, start_elem_id, incoming_side, bnd_id
     513             :   unsigned int total_size = 5;
     514             :   // Points
     515          34 :   total_size += num_points * 3;
     516             :   // Weights
     517          34 :   total_size += num_points;
     518             : 
     519          34 :   return total_size;
     520             : }
     521             : 
     522             : unsigned int
     523          17 : Packing<ViewFactorRayStudy::StartElem>::packed_size(typename std::vector<Real>::const_iterator in)
     524             : {
     525          17 :   const std::size_t num_points = *in++;
     526          17 :   return packing_size(num_points);
     527             : }
     528             : 
     529             : unsigned int
     530          17 : Packing<ViewFactorRayStudy::StartElem>::packable_size(
     531             :     const ViewFactorRayStudy::StartElem & start_elem, const void *)
     532             : {
     533             :   mooseAssert(start_elem._points.size() == start_elem._weights.size(), "Size mismatch");
     534          17 :   return packing_size(start_elem._points.size());
     535             : }
     536             : 
     537             : template <>
     538             : ViewFactorRayStudy::StartElem
     539          17 : Packing<ViewFactorRayStudy::StartElem>::unpack(std::vector<Real>::const_iterator in,
     540             :                                                ViewFactorRayStudy * study)
     541             : {
     542             :   // StartElem to fill into
     543             :   ViewFactorRayStudy::StartElem start_elem;
     544             : 
     545             :   // Number of points
     546          17 :   const std::size_t num_points = static_cast<std::size_t>(*in++);
     547             : 
     548             :   // Elem id
     549          17 :   RayTracingPackingUtils::unpack(start_elem._elem, *in++, &study->meshBase());
     550             : 
     551             :   // Start elem id
     552          17 :   RayTracingPackingUtils::unpack(start_elem._start_elem, *in++, &study->meshBase());
     553             : 
     554             :   // Incoming side
     555          17 :   start_elem._incoming_side = static_cast<unsigned short>(*in++);
     556             : 
     557             :   // Boundary ID
     558          17 :   start_elem._bnd_id = static_cast<BoundaryID>(*in++);
     559             : 
     560             :   // Points
     561          17 :   start_elem._points.resize(num_points);
     562         362 :   for (std::size_t i = 0; i < num_points; ++i)
     563             :   {
     564         345 :     start_elem._points[i](0) = *in++;
     565         345 :     start_elem._points[i](1) = *in++;
     566         345 :     start_elem._points[i](2) = *in++;
     567             :   }
     568             : 
     569             :   // Weights
     570          17 :   start_elem._weights.resize(num_points);
     571         362 :   for (std::size_t i = 0; i < num_points; ++i)
     572         345 :     start_elem._weights[i] = *in++;
     573             : 
     574          17 :   return start_elem;
     575             : }
     576             : 
     577             : template <>
     578             : void
     579          17 : Packing<ViewFactorRayStudy::StartElem>::pack(const ViewFactorRayStudy::StartElem & start_elem,
     580             :                                              std::back_insert_iterator<std::vector<Real>> data_out,
     581             :                                              const ViewFactorRayStudy * study)
     582             : {
     583             :   // Number of points
     584          17 :   data_out = static_cast<buffer_type>(start_elem._points.size());
     585             : 
     586             :   // Elem id
     587          34 :   data_out = RayTracingPackingUtils::pack<buffer_type>(start_elem._elem, &study->meshBase());
     588             : 
     589             :   // Start elem id
     590          34 :   data_out = RayTracingPackingUtils::pack<buffer_type>(start_elem._start_elem, &study->meshBase());
     591             : 
     592             :   // Incoming side
     593          17 :   data_out = static_cast<buffer_type>(start_elem._incoming_side);
     594             : 
     595             :   // Boundary id
     596          17 :   data_out = static_cast<buffer_type>(start_elem._bnd_id);
     597             : 
     598             :   // Points
     599         362 :   for (const auto & point : start_elem._points)
     600             :   {
     601             :     data_out = point(0);
     602             :     data_out = point(1);
     603             :     data_out = point(2);
     604             :   }
     605             : 
     606             :   // Weights
     607             :   std::copy(start_elem._weights.begin(), start_elem._weights.end(), data_out);
     608          17 : }
     609             : 
     610             : } // namespace Parallel
     611             : 
     612             : } // namespace libMesh

Generated by: LCOV version 1.14