LCOV - code coverage report
Current view: top level - src/raytracing - TraceRay.C (source / functions) Hit Total Coverage
Test: idaholab/moose ray_tracing: #31405 (292dce) with base fef103 Lines: 646 678 95.3 %
Date: 2025-09-04 07:56:07 Functions: 50 50 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 "TraceRay.h"
      11             : 
      12             : // Local includes
      13             : #include "Ray.h"
      14             : #include "RayBoundaryConditionBase.h"
      15             : #include "RayKernelBase.h"
      16             : #include "RayTracingStudy.h"
      17             : #include "TraceRayTools.h"
      18             : 
      19             : // libMesh includes
      20             : #include "libmesh/cell_tet4.h"
      21             : #include "libmesh/cell_tet10.h"
      22             : #include "libmesh/cell_tet14.h"
      23             : #include "libmesh/cell_hex8.h"
      24             : #include "libmesh/cell_hex20.h"
      25             : #include "libmesh/cell_hex27.h"
      26             : #include "libmesh/cell_prism6.h"
      27             : #include "libmesh/cell_prism15.h"
      28             : #include "libmesh/cell_prism18.h"
      29             : #include "libmesh/cell_pyramid5.h"
      30             : #include "libmesh/cell_pyramid13.h"
      31             : #include "libmesh/cell_pyramid14.h"
      32             : #include "libmesh/edge_edge2.h"
      33             : #include "libmesh/edge_edge3.h"
      34             : #include "libmesh/edge_edge4.h"
      35             : #include "libmesh/face_quad4.h"
      36             : #include "libmesh/face_quad8.h"
      37             : #include "libmesh/face_quad9.h"
      38             : #include "libmesh/face_tri3.h"
      39             : #include "libmesh/face_tri6.h"
      40             : #include "libmesh/face_tri7.h"
      41             : #include "libmesh/enum_to_string.h"
      42             : #include "libmesh/mesh.h"
      43             : 
      44             : using namespace libMesh;
      45             : using namespace TraceRayTools;
      46             : 
      47        4551 : TraceRay::TraceRay(RayTracingStudy & study, const THREAD_ID tid)
      48        4551 :   : _study(study),
      49        4551 :     _mesh(study.getSubProblem().mesh()),
      50        4551 :     _dim(_mesh.dimension()),
      51        4551 :     _boundary_info(_mesh.getMesh().get_boundary_info()),
      52        4551 :     _pid(_study.comm().rank()),
      53        4551 :     _tid(tid),
      54        4551 :     _backface_culling(false),
      55        4551 :     _current_normals(nullptr),
      56        9102 :     _results(ENDED_STATIONARY + 1)
      57             : {
      58        4551 : }
      59             : 
      60             : void
      61       10394 : TraceRay::preExecute()
      62             : {
      63       10394 :   _current_subdomain_id = Elem::invalid_subdomain_id;
      64       10394 :   _current_elem_type = INVALID_ELEM;
      65             : 
      66             :   // Zero out all results
      67      166304 :   for (auto & val : _results)
      68      155910 :     val = 0;
      69             : 
      70       10394 :   _has_ray_kernels = _study.hasRayKernels(_tid);
      71       10394 :   _is_rectangular_domain = _study.isRectangularDomain();
      72       10394 : }
      73             : 
      74             : void
      75         278 : TraceRay::meshChanged()
      76             : {
      77             :   // Invalidate the vertex and edge neighbor caches
      78             :   _vertex_neighbors.clear();
      79             :   _edge_neighbors.clear();
      80         278 : }
      81             : 
      82             : TraceRay::ExitsElemResult
      83    46867642 : TraceRay::exitsElem(const Elem * elem,
      84             :                     const ElemType elem_type,
      85             :                     const unsigned short incoming_side,
      86             :                     Point & intersection_point,
      87             :                     unsigned short & intersected_side,
      88             :                     ElemExtrema & intersected_extrema,
      89             :                     Real & intersection_distance,
      90             :                     const Point * normals)
      91             : {
      92             :   debugRay("Called exitsElem()");
      93             : 
      94             :   traceAssert(elem_type == elem->type(), "elem_type incorrect");
      95             :   traceAssert(intersection_point == RayTracingCommon::invalid_point, "Point should be invalid");
      96             :   traceAssert(intersected_side == RayTracingCommon::invalid_side, "Side should be invalid");
      97             :   traceAssert(intersected_extrema.isInvalid(), "Extrema should be invalid");
      98             :   traceAssert(intersection_distance == RayTracingCommon::invalid_distance,
      99             :               "Distance should be invalid");
     100             :   if (_study.verifyRays() && incoming_side != RayTracingCommon::invalid_side &&
     101             :       !_study.sideIsNonPlanar(elem, incoming_side))
     102             :     traceAssert(_study.sideIsIncoming(elem, incoming_side, (*_current_ray)->direction(), _tid),
     103             :                 "Incoming side is non-entrant");
     104             : 
     105             :   bool intersected;
     106    46867642 :   switch (elem_type)
     107             :   {
     108     5551245 :     case HEX8:
     109     5551245 :       intersected = exitsElem<Hex8, Hex8>(elem,
     110             :                                           incoming_side,
     111             :                                           intersection_point,
     112             :                                           intersected_side,
     113             :                                           intersected_extrema,
     114             :                                           intersection_distance,
     115             :                                           normals);
     116     5551245 :       break;
     117     7781387 :     case TET4:
     118     7781387 :       intersected = exitsElem<Tet4, Tet4>(elem,
     119             :                                           incoming_side,
     120             :                                           intersection_point,
     121             :                                           intersected_side,
     122             :                                           intersected_extrema,
     123             :                                           intersection_distance,
     124             :                                           normals);
     125     7781387 :       break;
     126     2531714 :     case PYRAMID5:
     127     2531714 :       intersected = exitsElem<Pyramid5, Pyramid5>(elem,
     128             :                                                   incoming_side,
     129             :                                                   intersection_point,
     130             :                                                   intersected_side,
     131             :                                                   intersected_extrema,
     132             :                                                   intersection_distance,
     133             :                                                   normals);
     134     2531714 :       break;
     135     1921082 :     case PRISM6:
     136     1921082 :       intersected = exitsElem<Prism6, Prism6>(elem,
     137             :                                               incoming_side,
     138             :                                               intersection_point,
     139             :                                               intersected_side,
     140             :                                               intersected_extrema,
     141             :                                               intersection_distance,
     142             :                                               normals);
     143     1921082 :       break;
     144      830453 :     case QUAD4:
     145      830453 :       intersected = exitsElem<Quad4, Quad4>(elem,
     146             :                                             incoming_side,
     147             :                                             intersection_point,
     148             :                                             intersected_side,
     149             :                                             intersected_extrema,
     150             :                                             intersection_distance,
     151             :                                             normals);
     152      830453 :       break;
     153       54055 :     case TRI3:
     154       54055 :       intersected = exitsElem<Tri3, Tri3>(elem,
     155             :                                           incoming_side,
     156             :                                           intersection_point,
     157             :                                           intersected_side,
     158             :                                           intersected_extrema,
     159             :                                           intersection_distance,
     160             :                                           normals);
     161       54055 :       break;
     162     1796521 :     case HEX20:
     163     1796521 :       intersected = exitsElem<Hex20, Hex8>(elem,
     164             :                                            incoming_side,
     165             :                                            intersection_point,
     166             :                                            intersected_side,
     167             :                                            intersected_extrema,
     168             :                                            intersection_distance,
     169             :                                            normals);
     170     1796521 :       break;
     171     1796510 :     case HEX27:
     172     1796510 :       intersected = exitsElem<Hex27, Hex8>(elem,
     173             :                                            incoming_side,
     174             :                                            intersection_point,
     175             :                                            intersected_side,
     176             :                                            intersected_extrema,
     177             :                                            intersection_distance,
     178             :                                            normals);
     179     1796510 :       break;
     180       29306 :     case QUAD8:
     181       29306 :       intersected = exitsElem<Quad8, Quad4>(elem,
     182             :                                             incoming_side,
     183             :                                             intersection_point,
     184             :                                             intersected_side,
     185             :                                             intersected_extrema,
     186             :                                             intersection_distance,
     187             :                                             normals);
     188       29306 :       break;
     189       29306 :     case QUAD9:
     190       29306 :       intersected = exitsElem<Quad9, Quad4>(elem,
     191             :                                             incoming_side,
     192             :                                             intersection_point,
     193             :                                             intersected_side,
     194             :                                             intersected_extrema,
     195             :                                             intersection_distance,
     196             :                                             normals);
     197       29306 :       break;
     198       44329 :     case TRI6:
     199       44329 :       intersected = exitsElem<Tri6, Tri3>(elem,
     200             :                                           incoming_side,
     201             :                                           intersection_point,
     202             :                                           intersected_side,
     203             :                                           intersected_extrema,
     204             :                                           intersection_distance,
     205             :                                           normals);
     206       44329 :       break;
     207       44329 :     case TRI7:
     208       44329 :       intersected = exitsElem<Tri7, Tri3>(elem,
     209             :                                           incoming_side,
     210             :                                           intersection_point,
     211             :                                           intersected_side,
     212             :                                           intersected_extrema,
     213             :                                           intersection_distance,
     214             :                                           normals);
     215       44329 :       break;
     216     7773339 :     case TET10:
     217     7773339 :       intersected = exitsElem<Tet10, Tet4>(elem,
     218             :                                            incoming_side,
     219             :                                            intersection_point,
     220             :                                            intersected_side,
     221             :                                            intersected_extrema,
     222             :                                            intersection_distance,
     223             :                                            normals);
     224     7773339 :       break;
     225     7773342 :     case TET14:
     226     7773342 :       intersected = exitsElem<Tet14, Tet4>(elem,
     227             :                                            incoming_side,
     228             :                                            intersection_point,
     229             :                                            intersected_side,
     230             :                                            intersected_extrema,
     231             :                                            intersection_distance,
     232             :                                            normals);
     233     7773342 :       break;
     234     2531456 :     case PYRAMID13:
     235     2531456 :       intersected = exitsElem<Pyramid13, Pyramid5>(elem,
     236             :                                                    incoming_side,
     237             :                                                    intersection_point,
     238             :                                                    intersected_side,
     239             :                                                    intersected_extrema,
     240             :                                                    intersection_distance,
     241             :                                                    normals);
     242     2531456 :       break;
     243     2531456 :     case PYRAMID14:
     244     2531456 :       intersected = exitsElem<Pyramid14, Pyramid5>(elem,
     245             :                                                    incoming_side,
     246             :                                                    intersection_point,
     247             :                                                    intersected_side,
     248             :                                                    intersected_extrema,
     249             :                                                    intersection_distance,
     250             :                                                    normals);
     251     2531456 :       break;
     252     1921074 :     case PRISM15:
     253     1921074 :       intersected = exitsElem<Prism15, Prism6>(elem,
     254             :                                                incoming_side,
     255             :                                                intersection_point,
     256             :                                                intersected_side,
     257             :                                                intersected_extrema,
     258             :                                                intersection_distance,
     259             :                                                normals);
     260     1921074 :       break;
     261     1921105 :     case PRISM18:
     262     1921105 :       intersected = exitsElem<Prism18, Prism6>(elem,
     263             :                                                incoming_side,
     264             :                                                intersection_point,
     265             :                                                intersected_side,
     266             :                                                intersected_extrema,
     267             :                                                intersection_distance,
     268             :                                                normals);
     269     1921105 :       break;
     270        4997 :     case EDGE2:
     271        4997 :       intersected = exitsElem<Edge2, Edge2>(elem,
     272             :                                             incoming_side,
     273             :                                             intersection_point,
     274             :                                             intersected_side,
     275             :                                             intersected_extrema,
     276             :                                             intersection_distance,
     277             :                                             normals);
     278        4997 :       break;
     279         318 :     case EDGE3:
     280         318 :       intersected = exitsElem<Edge3, Edge2>(elem,
     281             :                                             incoming_side,
     282             :                                             intersection_point,
     283             :                                             intersected_side,
     284             :                                             intersected_extrema,
     285             :                                             intersection_distance,
     286             :                                             normals);
     287         318 :       break;
     288         318 :     case EDGE4:
     289         318 :       intersected = exitsElem<Edge4, Edge2>(elem,
     290             :                                             incoming_side,
     291             :                                             intersection_point,
     292             :                                             intersected_side,
     293             :                                             intersected_extrema,
     294             :                                             intersection_distance,
     295             :                                             normals);
     296         318 :       break;
     297           0 :     default:
     298           0 :       mooseError(
     299           0 :           "Element type ", Utility::enum_to_string(elem->type()), " not supported by TraceRay");
     300             :   }
     301             : 
     302    46867642 :   if (intersected)
     303             :   {
     304    37267646 :     if (intersected_extrema.atExtrema())
     305             :       return intersected_extrema.atVertex() ? HIT_VERTEX : HIT_EDGE;
     306             :     return HIT_FACE;
     307             :   }
     308             : 
     309             :   return NO_EXIT;
     310             : }
     311             : 
     312             : template <typename T, typename FirstOrderT>
     313             : typename std::enable_if<!std::is_base_of<Edge, T>::value, bool>::type
     314    46862009 : TraceRay::exitsElem(const Elem * elem,
     315             :                     const unsigned short incoming_side,
     316             :                     Point & intersection_point,
     317             :                     unsigned short & intersected_side,
     318             :                     ElemExtrema & intersected_extrema,
     319             :                     Real & intersection_distance,
     320             :                     const Point * normals)
     321             : {
     322    46862009 :   ++_results[INTERSECTION_CALLS];
     323             : 
     324             :   debugRay("Called exitsElem() in 2D or 3D");
     325             : 
     326    46862009 :   const auto hmax = subdomainHmax(elem);
     327             : 
     328             :   // Current point and distance (maybe not the best!)
     329             :   Point current_intersection_point;
     330             :   Real current_intersection_distance;
     331             :   ElemExtrema current_intersected_extrema;
     332             :   // Scale the minimum intersection distance required by hmax
     333    46862009 :   Real best_intersection_distance = TRACE_TOLERANCE * hmax;
     334             :   // Whether or not we are going to try backface culling the first time around
     335             :   // This depends on if the user set it to use culling
     336    46862009 :   bool use_backface_culling = _backface_culling;
     337             :   // If all sides have failed to find an intersection, whether or not we
     338             :   // are going to see if the nonplanar skip side (only if it IS nonplanar)
     339             :   // is also an exiting point
     340             :   bool try_nonplanar_incoming_side = false;
     341             :   // Easy access into the current direction
     342    46862009 :   const auto & direction = (*_current_ray)->direction();
     343             :   // The current side that we're checking
     344             :   unsigned short s = 0;
     345             : 
     346             :   // Loop over the side loop. We need this in the cases that:
     347             :   // - Backface culling is enabled and we were not able to find an intersection
     348             :   //   so we will go through all culled sides and see if we can find one
     349             :   // - The incoming_side is non-planar and the ray also exits said side,
     350             :   //   which will be checked after all other sides fail
     351             :   while (true)
     352             :   {
     353             :     debugRay("  use_backface_culling = ", use_backface_culling);
     354             :     debugRay("  try_nonplanar_incoming_side = ", try_nonplanar_incoming_side);
     355             :     // Loop over all of the sides
     356             :     while (true)
     357             :     {
     358             :       debugRay("  Side ", s, " with centroid ", _elem_side_builder(*elem, s).vertex_average());
     359             : 
     360             :       // All of the checks that follow are done while we're still searching through
     361             :       // all of the sides. try_nonplanar_incoming_side is our last possible check
     362             :       // and does not involve looping through all of the sides, so skip these
     363             :       // checks if we're at that point.
     364   221942212 :       if (!try_nonplanar_incoming_side)
     365             :       {
     366             :         // Don't search backwards. If we have a non-planar incoming side, we will
     367             :         // check it if all other sides have failed
     368   221835506 :         if (s == incoming_side)
     369             :         {
     370             :           debugRay("    Skipping due to incoming side");
     371    44407307 :           if (++s == T::num_sides)
     372             :             break;
     373             :           else
     374    32743811 :             continue;
     375             :         }
     376             : 
     377             :         // Using backface culling on this run through the sides
     378             :         // If the direction is non-entrant, skip this side
     379   177428199 :         if (use_backface_culling)
     380             :         {
     381             :           // Side is non-entrant per the culling, so skip
     382     9214908 :           if (normals[s] * direction < -LOOSE_TRACE_TOLERANCE)
     383             :           {
     384             :             debugRay("    Skipping due to backface culling dot = ", normals[s] * direction);
     385             : 
     386     2973349 :             if (++s == T::num_sides)
     387             :               break;
     388             :             else
     389     2422158 :               continue;
     390             : 
     391             :             ++_results[BACKFACE_CULLING_SUCCESSES];
     392             :           }
     393             :         }
     394             :         // We're not using backface culling but it is enabled, which means
     395             :         // we're on our second run through the sides because we could not
     396             :         // find any intersections while culling the sides. Try again and
     397             :         // check the sides that we previously skipped for intersections
     398   168213291 :         else if (_backface_culling)
     399             :         {
     400             :           // Side was non-entrant per the culling, so try again
     401     2401920 :           if (normals[s] * direction >= -LOOSE_TRACE_TOLERANCE)
     402             :           {
     403             :             debugRay("    Skipping because we already checked this side with culling enabled");
     404     1568016 :             if (++s == T::num_sides)
     405             :               break;
     406             :             else
     407     1283281 :               continue;
     408             :           }
     409             :           else
     410             :             debugRay("    Side that was skipped due to culling");
     411             : 
     412      833904 :           ++_results[BACKFACE_CULLING_FAILURES];
     413             :         }
     414             :       }
     415             : 
     416             :       // Look for an intersection!
     417   172993540 :       current_intersection_point = RayTracingCommon::invalid_point;
     418             :       current_intersected_extrema.invalidate();
     419   172993540 :       const bool intersected = sideIntersectedByLine<FirstOrderT>(elem,
     420   172993540 :                                                                   _incoming_point,
     421             :                                                                   direction,
     422             :                                                                   s,
     423   172993540 :                                                                   _study.domainMaxLength(),
     424             :                                                                   current_intersection_point,
     425             :                                                                   current_intersection_distance,
     426             :                                                                   current_intersected_extrema,
     427             :                                                                   hmax
     428             : #ifdef DEBUG_RAY_INTERSECTIONS
     429             :                                                                   ,
     430             :                                                                   DEBUG_RAY_IF
     431             : #endif
     432             :       );
     433             : 
     434             :       // Do they intersect and is it further down the path than any other intersection?
     435             :       // If so, keep track of the intersection with the furthest distance
     436   172993540 :       if (intersected)
     437             :       {
     438             :         debugRay("    Intersected at point ",
     439             :                  current_intersection_point,
     440             :                  " with distance ",
     441             :                  current_intersection_distance);
     442             :         debugRay("    Best intersection distance = ", best_intersection_distance);
     443             : 
     444             : #ifndef NDEBUG
     445             :         // Only validate intersections if the side is planar
     446             :         if (_study.verifyTraceIntersections() && !_study.sideIsNonPlanar(elem, s) &&
     447             :             !_elem_side_builder(*elem, s).contains_point(current_intersection_point))
     448             :           failTrace("Intersected side does not contain intersection point",
     449             :                     _study.tolerateFailure(),
     450             :                     __LINE__);
     451             : #endif
     452             : 
     453             :         // The intersection we just computed is further than any other intersection
     454             :         // that was found so far - mark it as the best
     455    40772661 :         if (current_intersection_distance > best_intersection_distance)
     456             :         {
     457             :           debugRay("    Best intersection so far");
     458             : 
     459    37909849 :           intersected_side = s;
     460    37909849 :           intersection_distance = current_intersection_distance;
     461    37909849 :           intersection_point = current_intersection_point;
     462             :           intersected_extrema = current_intersected_extrema;
     463             :           best_intersection_distance = current_intersection_distance;
     464             :         }
     465             :       }
     466             : 
     467   172993540 :       if (++s == T::num_sides || try_nonplanar_incoming_side)
     468             :         break;
     469             :       else
     470   138042423 :         continue;
     471             :     } // while(true)
     472             : 
     473             :     // Found an intersection
     474    47450539 :     if (intersected_side != RayTracingCommon::invalid_side)
     475             :     {
     476             :       debugRay("  Exiting with intersection");
     477             :       debugRay("    intersected_side = ", intersected_side);
     478             :       debugRay("    intersection_distance = ", intersection_distance);
     479             :       debugRay("    intersection_point = ", intersection_point);
     480             :       debugRay("    intersected_extrema = ", intersected_extrema);
     481             : 
     482             :       return true;
     483             :     }
     484             : 
     485             :     // This was our last possible check - no dice!
     486    10188508 :     if (try_nonplanar_incoming_side)
     487             :       return false;
     488             : 
     489             :     // We didn't find an intersection but we used backface culling. Try again without
     490             :     // it, checking only the sides that we skipped due to culling
     491    10081802 :     if (use_backface_culling)
     492             :     {
     493             :       debugRay("  Didn't find an intersection, retrying without backface culling");
     494             :       use_backface_culling = false;
     495             :       s = 0;
     496      481824 :       continue;
     497             :     }
     498             : 
     499             :     // Have tried all sides (potentially with and without culling). If the incoming
     500             :     // side is valid is non planar, see if we also exit out of it
     501     9599978 :     if (_incoming_side != RayTracingCommon::invalid_side &&
     502     9595583 :         _study.sideIsNonPlanar(elem, incoming_side))
     503             :     {
     504             :       debugRay("  Didn't find an intersection, trying non-planar incoming_side");
     505             :       try_nonplanar_incoming_side = true;
     506             :       s = incoming_side;
     507      106706 :       continue;
     508             :     }
     509             : 
     510             :     // No intersection found!
     511             :     return false;
     512             :   } // while(true)
     513             : }
     514             : 
     515             : template <typename T, typename FirstOrderT>
     516             : typename std::enable_if<std::is_base_of<Edge, T>::value, bool>::type
     517        5633 : TraceRay::exitsElem(const Elem * elem,
     518             :                     const unsigned short incoming_side,
     519             :                     Point & intersection_point,
     520             :                     unsigned short & intersected_side,
     521             :                     ElemExtrema & intersected_extrema,
     522             :                     Real & intersection_distance,
     523             :                     const Point *)
     524             : {
     525        5633 :   ++_results[INTERSECTION_CALLS];
     526             : 
     527             :   debugRay("Called exitsElem() in 1D");
     528             : 
     529             :   // Scale the tolerance based on the element size
     530        5633 :   const auto tol = subdomainHmax(elem) * TRACE_TOLERANCE;
     531             : 
     532             :   // Can quickly return if we have an incoming side
     533             :   // There's only one other choice in 1D!
     534        5633 :   if (incoming_side != RayTracingCommon::invalid_side)
     535             :   {
     536        5157 :     intersected_side = (incoming_side == 1 ? 0 : 1);
     537             :     intersected_extrema.setVertex(intersected_side);
     538        5157 :     intersection_point = elem->point(intersected_side);
     539        5157 :     intersection_distance = (_incoming_point - intersection_point).norm();
     540             : 
     541             :     debugRay("  Incoming side is set to ", incoming_side, " so setting to other side");
     542             :     debugRay("  Intersected side ", intersected_side, " at ", intersection_point);
     543             : 
     544        5157 :     return true;
     545             :   }
     546             : 
     547             :   // End point that is for sure out of the element
     548             :   const Point extended_end_point =
     549         476 :       _incoming_point + _study.domainMaxLength() * (*_current_ray)->direction();
     550             : 
     551             :   // We're looking for the side whose point lays between the incoming point and the
     552             :   // extended end point
     553         784 :   for (MooseIndex(elem->n_sides()) side = 0; side < elem->n_sides(); ++side)
     554             :   {
     555         766 :     const Point side_point = elem->point(side);
     556             :     debugRay("  Checking side ", side, " at ", side_point);
     557             : 
     558         766 :     const Real incoming_to_side = (side_point - _incoming_point).norm();
     559         766 :     if (incoming_to_side < tol)
     560             :     {
     561             :       debugRay("    Continuing because at side");
     562         200 :       continue;
     563             :     }
     564             : 
     565         566 :     const Real incoming_to_end = (extended_end_point - _incoming_point).norm();
     566         566 :     const Real side_to_end = (extended_end_point - side_point).norm();
     567         566 :     const Real sum = incoming_to_side + side_to_end - incoming_to_end;
     568             :     debugRay("    Sum = ", sum);
     569             : 
     570         566 :     if (std::abs(sum) < tol)
     571             :     {
     572         458 :       intersected_side = side;
     573             :       intersected_extrema.setVertex(side);
     574         458 :       intersection_point = side_point;
     575         458 :       intersection_distance = incoming_to_side;
     576             :       debugRay("    Intersected at ", intersection_point);
     577         458 :       return true;
     578             :     }
     579             :   }
     580             : 
     581             :   return false;
     582             : }
     583             : 
     584             : TraceRay::ExitsElemResult
     585     3803893 : TraceRay::moveThroughNeighbors(const std::vector<NeighborInfo> & neighbors,
     586             :                                const Elem * last_elem,
     587             :                                const Elem *& best_elem,
     588             :                                unsigned short & best_elem_incoming_side)
     589             : {
     590     3803893 :   ++_results[MOVED_THROUGH_NEIGHBORS];
     591             : 
     592             :   debugRay("Called moveThroughNeighbors() with ", neighbors.size(), " neighbors, and:");
     593             :   debugRay("  last_elem->id() = ", last_elem ? last_elem->id() : DofObject::invalid_id);
     594             :   debugRay("  _incoming_point = ", _incoming_point);
     595             : 
     596             :   traceAssert(!best_elem, "Best elem should be null");
     597             :   traceAssert(best_elem_incoming_side == RayTracingCommon::invalid_side,
     598             :               "Best elem side should be invalid");
     599             :   traceAssert(_intersection_point == RayTracingCommon::invalid_point, "Point should be invalid");
     600             :   traceAssert(_intersected_side == RayTracingCommon::invalid_side, "Side should be invalid");
     601             :   traceAssert(_intersected_extrema.isInvalid(), "Extrema should be invalid");
     602             :   traceAssert(_intersection_distance == RayTracingCommon::invalid_distance,
     603             :               "Distance should be invalid");
     604             : 
     605             :   // Marker for the longest ray segment distance we've found in a neighbor. Start with a quite
     606             :   // small number because at this point we're desperate and will take anything!
     607             :   Real longest_distance = 1.0e-12;
     608             :   // Temporaries for the intersection checks
     609             :   unsigned short current_incoming_side;
     610             :   Point current_intersection_point;
     611             :   unsigned short current_intersected_side;
     612             :   ElemExtrema current_intersected_extrema;
     613             :   Real current_intersection_distance;
     614             :   TraceRay::ExitsElemResult best_exit_result = NO_EXIT;
     615             : 
     616             :   // The NeighborInfo for the last_elem
     617             :   // (to store so we can try it later if we fail for everyone else)
     618             :   const NeighborInfo * last_elem_info = nullptr;
     619             : 
     620    22729628 :   for (const NeighborInfo & neighbor_info : neighbors)
     621             :   {
     622             :     // If we're at the element we want to do last, skip it and store the info so
     623             :     // we can get to it later in case we fail at finding anything
     624    18925735 :     if (neighbor_info._elem == last_elem)
     625             :     {
     626             :       debugRay("Skipping last elem ", last_elem->id());
     627             :       last_elem_info = &neighbor_info;
     628     3806736 :       continue;
     629             :     }
     630             : 
     631    15118999 :     const auto exit_result = moveThroughNeighbor(neighbor_info,
     632             :                                                  current_incoming_side,
     633             :                                                  current_intersection_point,
     634             :                                                  current_intersected_side,
     635             :                                                  current_intersected_extrema,
     636             :                                                  current_intersection_distance);
     637             : 
     638             :     // Found one! See if it's the best way
     639    15118999 :     if (exit_result != NO_EXIT)
     640             :     {
     641             :       debugRay("Ray can exit through neighbor ", neighbor_info._elem->id());
     642             : 
     643     5530808 :       if (current_intersection_distance > longest_distance)
     644             :       {
     645     3914385 :         best_elem = neighbor_info._elem;
     646     3914385 :         best_elem_incoming_side = current_incoming_side;
     647     3914385 :         _intersection_point = current_intersection_point;
     648     3914385 :         _intersected_side = current_intersected_side;
     649             :         _intersected_extrema = current_intersected_extrema;
     650     3914385 :         _intersection_distance = current_intersection_distance;
     651             :         longest_distance = current_intersection_distance;
     652             :         best_exit_result = exit_result;
     653             :       }
     654             :     }
     655             :   }
     656             : 
     657             :   // Didn't find someone to exit, so try the last_elem (if any)
     658     3803893 :   if (!best_elem && last_elem_info)
     659             :   {
     660         414 :     const auto exit_result = moveThroughNeighbor(*last_elem_info,
     661             :                                                  current_incoming_side,
     662             :                                                  current_intersection_point,
     663             :                                                  current_intersected_side,
     664             :                                                  current_intersected_extrema,
     665             :                                                  current_intersection_distance);
     666             : 
     667         414 :     if (exit_result != NO_EXIT && current_intersection_distance > longest_distance)
     668             :     {
     669             :       debugRay("Ray can exit through last_elem ", last_elem->id());
     670             : 
     671         414 :       best_elem = last_elem;
     672         414 :       best_elem_incoming_side = current_incoming_side;
     673         414 :       _intersection_point = current_intersection_point;
     674         414 :       _intersected_side = current_intersected_side;
     675             :       _intersected_extrema = current_intersected_extrema;
     676         414 :       _intersection_distance = current_intersection_distance;
     677             :       best_exit_result = exit_result;
     678             :     }
     679             :   }
     680             : 
     681             :   debugRay("moveThroughNeighbors() best result:");
     682             :   debugRay("  best_elem = ", best_elem ? best_elem->id() : DofObject::invalid_id);
     683             :   debugRay("  best_elem_incoming_side = ", best_elem_incoming_side);
     684             :   debugRay("  _intersection_point = ", _intersection_point);
     685             :   debugRay("  _intersected_side = ", _intersected_side);
     686             :   debugRay("  _intersected_extrema = ", _intersected_extrema);
     687             :   debugRay("  _intersection_distance = ", _intersection_distance);
     688             :   if (best_elem)
     689             :   {
     690             :     debugRay("moveThroughNeighbors() next neighbor elem info:");
     691             :     debugRay(best_elem->get_info());
     692             :   }
     693             : 
     694     3803893 :   return best_exit_result;
     695             : }
     696             : 
     697             : TraceRay::ExitsElemResult
     698    15119413 : TraceRay::moveThroughNeighbor(const NeighborInfo & neighbor_info,
     699             :                               unsigned short & incoming_side,
     700             :                               Point & intersection_point,
     701             :                               unsigned short & intersected_side,
     702             :                               ElemExtrema & intersected_extrema,
     703             :                               Real & intersection_distance)
     704             : {
     705    15119413 :   if (!neighbor_info._valid)
     706             :     return NO_EXIT;
     707             : 
     708    15118121 :   const Elem * neighbor = neighbor_info._elem;
     709             :   debugRay("Checking neighbor ", neighbor->id(), " with centroid ", neighbor->vertex_average());
     710             : 
     711             :   // Find an entrant side (if any)
     712    15118121 :   incoming_side = RayTracingCommon::invalid_side;
     713    19929218 :   for (MooseIndex(neighbor_info._sides.size()) i = 0; i < neighbor_info._sides.size(); ++i)
     714    19925777 :     if (neighbor_info._side_normals[i] * (*_current_ray)->direction() < LOOSE_TRACE_TOLERANCE)
     715             :     {
     716    15114680 :       incoming_side = neighbor_info._sides[i];
     717    15114680 :       break;
     718             :     }
     719             : 
     720             :   // No entrant sides on this neighbor were found
     721    15118121 :   if (incoming_side == RayTracingCommon::invalid_side)
     722             :     return NO_EXIT;
     723             : 
     724    15114680 :   intersection_point = RayTracingCommon::invalid_point;
     725    15114680 :   intersected_side = RayTracingCommon::invalid_side;
     726             :   intersected_extrema.invalidate();
     727    15114680 :   intersection_distance = RayTracingCommon::invalid_distance;
     728             : 
     729             :   // See if there is a way through the neighbor element
     730             :   debugRay("Called exitsElem() from moveThroughNeighbor()");
     731             :   const auto exit_result =
     732    15114680 :       exitsElem(neighbor,
     733    15114680 :                 neighbor->type(),
     734    15114680 :                 incoming_side,
     735             :                 intersection_point,
     736             :                 intersected_side,
     737             :                 intersected_extrema,
     738             :                 intersection_distance,
     739    15114680 :                 _backface_culling ? _study.getElemNormals(neighbor, _tid) : nullptr);
     740             :   debugRay("Done with exitsElem() from moveThroughNeighbor()");
     741             : 
     742    15114680 :   return exit_result;
     743             : }
     744             : 
     745             : void
     746     1410360 : TraceRay::applyOnExternalBoundary(const std::shared_ptr<Ray> & ray)
     747             : {
     748             :   debugRay("Called applyOnExternalBoundary() with");
     749             :   debugRay("  _current_elem->id() = ", _current_elem->id());
     750             :   debugRay("  _intersection_point = ", _intersection_point);
     751             :   debugRay("  _intersected_side = ", _intersected_side);
     752             :   debugRay("  _intersected_extrema = ", _intersected_extrema);
     753             : 
     754             :   // Clear storage for the list of ConstBndElement that we need to apply RayBCs to
     755     1410360 :   _boundary_elems.clear();
     756             : 
     757             :   // If on the elem extrema (at a vertex or within an edge), check for external sidesets on
     758             :   // neighbors (which will include _current_elem)
     759     1410360 :   if (_dim != 1 && _intersected_extrema.atExtrema())
     760             :   {
     761      836807 :     const auto & neighbors = getNeighbors(_current_elem, _intersected_extrema, _intersection_point);
     762             :     debugRay("  Found ", neighbors.size(), " vertex/edge neighbors (including self)");
     763             :     traceAssert(std::count_if(neighbors.begin(),
     764             :                               neighbors.end(),
     765             :                               [this](const NeighborInfo & ni)
     766             :                               { return ni._elem == _current_elem; }),
     767             :                 "_current_elem not in neighbors");
     768             : 
     769     3206576 :     for (const auto & neighbor_info : neighbors)
     770             :     {
     771     2369769 :       if (!neighbor_info._valid)
     772           0 :         continue;
     773             : 
     774     2369769 :       const Elem * elem = neighbor_info._elem;
     775             :       const auto & sides = neighbor_info._sides;
     776             :       const auto & side_normals = neighbor_info._side_normals;
     777             : 
     778     8415173 :       for (MooseIndex(side_normals.size()) i = 0; i < side_normals.size(); ++i)
     779     6045404 :         if (!elem->neighbor_ptr(sides[i]) // is a boundary side that has our point
     780     6045404 :             && side_normals[i] * ray->direction() > TRACE_TOLERANCE) // and is entrant
     781             :         {
     782             :           // TODO: this could likely be optimized
     783             :           ElemExtrema extrema;
     784     1983839 :           withinExtremaOnSide(elem, _intersection_point, sides[i], _dim, extrema);
     785             : 
     786     1983839 :           _boundary_info.boundary_ids(elem, sides[i], _boundary_ids);
     787     1983839 :           possiblyAddToBoundaryElems(elem, sides[i], _boundary_ids, extrema);
     788             :         }
     789             :     }
     790             :   }
     791             :   // Not on the periphery (at vertex/edge), so we just need to add the external
     792             :   // sidesets from _current_elem on _intersected_side
     793             :   else
     794             :   {
     795      573553 :     _boundary_info.boundary_ids(_current_elem, _intersected_side, _boundary_ids);
     796      573553 :     possiblyAddToBoundaryElems(
     797      573553 :         _current_elem, _intersected_side, _boundary_ids, _intersected_extrema);
     798             :   }
     799             : 
     800             :   debugRay("Calling external onBoundary() with ", _boundary_elems.size(), " boundaries");
     801     1410360 :   onBoundary(ray, /* external = */ true);
     802     1410350 : }
     803             : 
     804             : void
     805        2506 : TraceRay::applyOnInternalBoundary(const std::shared_ptr<Ray> & ray)
     806             : {
     807             :   traceAssert(_last_elem, "Must be valid");
     808             : 
     809             :   debugRay("Called applyOnInternalBoundary() with");
     810             :   debugRay("  intersection_point = ", _intersection_point);
     811             :   debugRay("  _current_elem->id() = ", _current_elem->id());
     812             :   debugRay("  _incoming_side = ", _incoming_side);
     813             :   debugRay("  _last_elem->id() = ", _last_elem->id());
     814             :   debugRay("  _intersected_side = ", _intersected_side);
     815             :   debugRay("  _intersected_extrema = ", _intersected_extrema);
     816             :   traceAssert(_study.hasInternalSidesets(), "Do not have internal sidesets");
     817             :   traceAssert(_intersection_point.absolute_fuzzy_equals(_incoming_point, TRACE_TOLERANCE),
     818             :               "Intersection and incoming points should be the same");
     819             : 
     820             :   // Clear storage for the list of ConstBndElement that we need to apply RayBCs to
     821        2506 :   _boundary_elems.clear();
     822             : 
     823             :   ElemExtrema temp_extrema;
     824             : 
     825             :   // If on the elem extrema (at a vertex or within an edge), we need to check for internal
     826             :   // sidesets on neighbors (which will include _last_elem and _current_elem)
     827        2506 :   if (_dim != 1 && _intersected_extrema.atExtrema())
     828             :   {
     829             :     debugRay("Checking point neighbors for internal sidesets");
     830             : 
     831             :     // Get the neighbors
     832        1728 :     const auto & neighbors = getNeighbors(_last_elem, _intersected_extrema, _intersection_point);
     833             :     debugRay("  Found ", neighbors.size(), " vertex/edge neighbors");
     834             :     traceAssert(std::count_if(neighbors.begin(),
     835             :                               neighbors.end(),
     836             :                               [this](const NeighborInfo & ni)
     837             :                               { return ni._elem == _current_elem || ni._elem == _last_elem; }) == 2,
     838             :                 "_current_elem/_last_elem not in neighbors");
     839             : 
     840       15354 :     for (const auto & neighbor_info : neighbors)
     841             :     {
     842       13626 :       if (!neighbor_info._valid)
     843           0 :         continue;
     844             : 
     845       13626 :       const Elem * elem = neighbor_info._elem;
     846             : 
     847             :       // Grab the internal sidesets for this elem
     848       13626 :       const auto & sidesets = _study.getInternalSidesets(elem);
     849             :       // It has none to contribute, so we can continue
     850       13626 :       if (sidesets.empty())
     851             :       {
     852             :         debugRay("    Elem ", elem->id(), " has no internal sidesets");
     853       11142 :         continue;
     854             :       }
     855             : 
     856             :       // The sides on this elem that contain our point and their outward normals
     857             :       const auto & sides = neighbor_info._sides;
     858             :       const auto & side_normals = neighbor_info._side_normals;
     859             : 
     860             :       // See if any of the internal sidesets are relevant to this point
     861        8946 :       for (std::size_t i = 0; i < sides.size(); ++i)
     862             :       {
     863        6462 :         const auto side = sides[i];
     864             :         // Side has internal sidesets and is entrant
     865        6462 :         if (sidesets[side].size() && std::abs(side_normals[i] * ray->direction()) > TRACE_TOLERANCE)
     866             :         {
     867             :           // TODO: this could likely be optimized
     868             :           temp_extrema.invalidate();
     869        1710 :           withinExtremaOnSide(elem, _intersection_point, side, _dim, temp_extrema);
     870             : 
     871        1710 :           possiblyAddToBoundaryElems(elem, side, sidesets[side], temp_extrema);
     872             :         }
     873             :       }
     874             :     }
     875             :   }
     876             :   // Not on the periphery (at vertex/edge), so we just need to add the sidesets from _current_elem
     877             :   // on _incoming_side and _last_elem on _intersected_side
     878             :   else
     879             :   {
     880         778 :     const auto & current_elem_sidesets = _study.getInternalSidesets(_current_elem);
     881         778 :     if (current_elem_sidesets.size() && current_elem_sidesets[_incoming_side].size())
     882             :     {
     883             :       // This is possible but we need extrema checks on _incoming_elem to pass to the
     884             :       // boundary conditions. For now, we just pass in _intersected_extrema which could
     885             :       // be very wrong with adaptivity but is correct without it. I struggled with getting
     886             :       // the actual extrema checks and we're not using this now so I bet this error will
     887             :       // come back to haunt me in the future :-)
     888         272 :       if (!_study.hasSameLevelActiveElems())
     889           0 :         mooseError("Internal sidesets are not currently supported with adaptivity in tracing");
     890             : 
     891             :       // Special case for 1D
     892         272 :       if (_dim == 1)
     893          38 :         temp_extrema.setVertex(atVertex(_current_elem, _intersection_point));
     894             : 
     895         272 :       possiblyAddToBoundaryElems(_current_elem,
     896             :                                  _incoming_side,
     897         272 :                                  current_elem_sidesets[_incoming_side],
     898         272 :                                  _dim != 1 ? _intersected_extrema : temp_extrema);
     899             :     }
     900             : 
     901         778 :     const auto & last_elem_sidesets = _study.getInternalSidesets(_last_elem);
     902         778 :     if (last_elem_sidesets.size() && last_elem_sidesets[_intersected_side].size())
     903         308 :       possiblyAddToBoundaryElems(_last_elem,
     904             :                                  _intersected_side,
     905             :                                  last_elem_sidesets[_intersected_side],
     906         308 :                                  _intersected_extrema);
     907             :   }
     908             : 
     909        2506 :   if (!_boundary_elems.empty())
     910             :   {
     911             :     debugRay("  Calling internal onBoundary() with ", _boundary_elems.size(), " boundaries");
     912        1219 :     onBoundary(ray, /* external = */ false);
     913             :   }
     914        2502 : }
     915             : 
     916             : void
     917     2559682 : TraceRay::possiblyAddToBoundaryElems(const Elem * elem,
     918             :                                      const unsigned short side,
     919             :                                      const std::vector<BoundaryID> & bnd_ids,
     920             :                                      const ElemExtrema & extrema)
     921             : {
     922             :   if (!_study.sideIsNonPlanar(elem, side))
     923             :     traceAssert(extrema.isValid(elem, _intersection_point), "Extrema not correct");
     924             : 
     925     5119364 :   for (const auto bnd_id : bnd_ids)
     926             :   {
     927             :     bool found = false;
     928     2713309 :     for (const auto & bnd_elem : _boundary_elems)
     929     1208041 :       if (bnd_elem.bnd_id == bnd_id)
     930             :       {
     931             :         found = true;
     932             :         break;
     933             :       }
     934             : 
     935     2559682 :     if (!found)
     936             :     {
     937             :       debugRay("  Need to apply boundary on elem ",
     938             :                elem->id(),
     939             :                " and side ",
     940             :                side,
     941             :                " for bnd_id ",
     942             :                bnd_id);
     943             : 
     944     1505268 :       _boundary_elems.emplace_back(elem, side, bnd_id, extrema);
     945             :     }
     946             :   }
     947     2559682 : }
     948             : 
     949             : void
     950     1117190 : TraceRay::findExternalBoundarySide(unsigned short & boundary_side,
     951             :                                    ElemExtrema & boundary_extrema,
     952             :                                    const Elem *& boundary_elem)
     953             : {
     954             :   traceAssert(_current_elem->neighbor_ptr(_intersected_side), "Already on boundary");
     955             :   traceAssert(boundary_side == RayTracingCommon::invalid_side, "Side should be invalid");
     956             :   traceAssert(boundary_extrema.isInvalid(), "Extrema should be invalid");
     957             :   traceAssert(!boundary_elem, "Elem should be invalid");
     958             :   traceAssert(_current_elem->dim() != 1, "1D traces shouldn't make it here");
     959             :   traceAssert(_current_elem->n_sides() == _current_elem_n_sides, "_current_elem_n_sides incorrect");
     960             :   traceAssert(_intersected_extrema.atExtrema(), "Should be at extrema");
     961             :   debugRay("Called findExternalBoundarySide() on side ", _intersected_side);
     962             :   debugRay("  _intersected_side = ", _intersected_side);
     963             :   debugRay("  _intersected_extrema", _intersected_extrema);
     964             : 
     965     1117190 :   const auto & direction = (*_current_ray)->direction();
     966             :   const auto at_edge = _intersected_extrema.atEdge();
     967             : 
     968             :   // First, look for other sides on _current_elem that touch the intersected vertex/edge
     969             :   // that are on the boundary and are outgoing
     970     6376084 :   for (unsigned short s = 0; s < _current_elem_n_sides; ++s)
     971     6784724 :     if (!_current_elem->neighbor_ptr(s) && s != _intersected_side &&
     972     2489073 :         _current_elem->is_node_on_side(_intersected_extrema.first, s) &&
     973     7377325 :         (!at_edge || _current_elem->is_node_on_side(_intersected_extrema.second, s)) &&
     974     1062808 :         !_study.sideIsIncoming(_current_elem, s, direction, _tid))
     975             :     {
     976             :       debugRay("  Side ", s, " is a boundary side and the Ray exits");
     977      172018 :       boundary_side = s;
     978             :       boundary_extrema = _intersected_extrema;
     979      172018 :       boundary_elem = _current_elem;
     980      172018 :       return;
     981             :     }
     982             : 
     983             :   // No luck in our element, so see if any neighbors at this vertex/edge are
     984             :   // on the boundary and are outgoing
     985      945172 :   const auto & neighbors = getNeighbors(_current_elem, _intersected_extrema, _intersection_point);
     986             :   debugRay("Checking current element failed, now checking neighbors");
     987             : 
     988             :   debugRay("Found ", neighbors.size(), " candidate neighbors (including self)");
     989     3267297 :   for (const auto & neighbor_info : neighbors)
     990             :   {
     991             :     // This will be false when we have an edge neighbor that isn't a neighbor at
     992             :     // _intersection_point
     993     2484948 :     if (!neighbor_info._valid)
     994           0 :       continue;
     995             : 
     996     2484948 :     const Elem * neighbor = neighbor_info._elem;
     997             :     // We've already checked ourself
     998     2484948 :     if (neighbor == _current_elem)
     999      851710 :       continue;
    1000             : 
    1001             :     debugRay("Checking neighbor ", neighbor->id());
    1002             : 
    1003             :     // Loop through the sides we touch and look for one that this Ray exits and is on the boundary
    1004     5076268 :     for (MooseIndex(neighbor_info._sides.size()) i = 0; i < neighbor_info._sides.size(); ++i)
    1005     3605853 :       if (neighbor_info._side_normals[i] * direction > TRACE_TOLERANCE &&
    1006      795137 :           !neighbor->neighbor_ptr(neighbor_info._sides[i]))
    1007             :       {
    1008      162823 :         withinExtremaOnSide(
    1009      162823 :             neighbor, _intersection_point, neighbor_info._sides[i], _dim, boundary_extrema);
    1010             :         traceAssert(boundary_extrema.atExtrema(), "Should be at extrema");
    1011      162823 :         boundary_side = neighbor_info._sides[i];
    1012      162823 :         boundary_elem = neighbor;
    1013             :         return;
    1014             :       }
    1015             :   }
    1016             : }
    1017             : 
    1018             : void
    1019     5380196 : TraceRay::trace(const std::shared_ptr<Ray> & ray)
    1020             : {
    1021             :   mooseAssert(_study.currentlyPropagating(), "Should only use while propagating rays");
    1022             : 
    1023     5380196 :   _current_ray = &ray;
    1024     5380196 :   _current_elem = ray->currentElem();
    1025     5380196 :   _last_elem = nullptr;
    1026     5380196 :   _incoming_point = ray->currentPoint();
    1027     5380196 :   _incoming_side = ray->currentIncomingSide();
    1028     5380196 :   _should_continue = true;
    1029             : 
    1030     5380196 :   _study.preTrace(_tid, ray);
    1031             : 
    1032             :   traceAssert(_current_elem, "Current element is not set");
    1033             :   traceAssert(_current_elem->active(), "Current element is not active");
    1034             :   traceAssert(!ray->invalidCurrentPoint(), "Current point is invalid");
    1035             :   traceAssert(ray->shouldContinue(), "Ray should not continue");
    1036     5380196 :   if (_study.verifyRays() && !ray->invalidCurrentIncomingSide() && ray->maxDistance() > 0 &&
    1037    10209177 :       !_study.sideIsNonPlanar(_current_elem, _incoming_side) &&
    1038     2386714 :       !_study.sideIsIncoming(_current_elem, _incoming_side, ray->direction(), _tid))
    1039           0 :     failTrace("Ray incoming side is not incoming", /* warning = */ false, __LINE__);
    1040             : 
    1041             : #ifdef DEBUG_RAY_IF
    1042             :   if (DEBUG_RAY_IF)
    1043             :     libMesh::err << "\n\n";
    1044             : #endif
    1045             :   debugRay("At top of trace for Ray");
    1046             :   debugRay("Top of trace loop Ray info\n", ray->getInfo());
    1047             :   debugRay("Top of trace loop starting elem info\n", ray->currentElem()->get_info());
    1048             : 
    1049             :   // Invalidate this up front because it's copied immedtiately into _last_intersected_extrema
    1050             :   _intersected_extrema.invalidate();
    1051             : 
    1052             : #ifdef DEBUG_RAY_MESH_IF
    1053             :   _debug_mesh = nullptr;
    1054             :   _debug_node_count = 0;
    1055             :   if (DEBUG_RAY_MESH_IF)
    1056             :   {
    1057             :     _debug_mesh = new Mesh(_debug_comm, _dim);
    1058             :     _debug_mesh->skip_partitioning(true);
    1059             :   }
    1060             : #endif
    1061             : 
    1062             :   // Caching trace along the way: init for this Ray
    1063     5380196 :   if (_study.shouldCacheTrace(ray))
    1064             :   {
    1065             :     debugRay("Trying to init threaded cached trace");
    1066             : 
    1067       13787 :     _current_cached_trace = &_study.initThreadedCachedTrace(ray, _tid);
    1068             : 
    1069             :     // Add starting data
    1070       13787 :     if (_study.dataOnCacheTraces())
    1071        1371 :       _current_cached_trace->lastPoint()._data = ray->data();
    1072       13787 :     if (_study.auxDataOnCacheTraces())
    1073        1325 :       _current_cached_trace->lastPoint()._aux_data = ray->auxData();
    1074             :   }
    1075             :   else
    1076     5366409 :     _current_cached_trace = nullptr;
    1077             : 
    1078             :   // Need to call subdomain setup
    1079     5380196 :   if (_current_elem->subdomain_id() != _current_subdomain_id || _study.rayDependentSubdomainSetup())
    1080     5380196 :     onSubdomainChanged(ray, /* same_ray = */ false);
    1081             :   // If we didn't change subdomains, we still need to call this on the RayKernels
    1082             :   else
    1083           0 :     for (RayKernelBase * rk : _study.currentRayKernels(_tid))
    1084           0 :       rk->preTrace();
    1085             : 
    1086             :   // Ray tracing loop through each segment for a single Ray on this processor
    1087             :   do
    1088             :   {
    1089             : #ifdef DEBUG_RAY_IF
    1090             :     if (DEBUG_RAY_IF)
    1091             :       libMesh::err << "\n\n";
    1092             : #endif
    1093             :     debugRay("At top of ray tracing loop");
    1094             :     debugRay("  ray->id() = ", ray->id());
    1095             :     debugRay("  _incoming_point = ", _incoming_point);
    1096             :     debugRay("  _incoming_side = ", _incoming_side);
    1097             :     debugRay("  _current_elem->id() = ", _current_elem->id());
    1098             :     debugRay("  _current_elem->subdomain_id() = ", _current_elem->subdomain_id());
    1099             :     debugRay("  _current_subdomain_id = ", _current_subdomain_id);
    1100             :     debugRay("  _current_elem_type = ", Utility::enum_to_string(_current_elem_type));
    1101             :     debugRay("Top of ray tracing loop Ray info\n", ray->getInfo());
    1102             :     debugRay("Top of ray tracing loop current elem info\n", _current_elem->get_info());
    1103             : 
    1104             :     traceAssert(_current_ray == &ray, "Current ray mismatch");
    1105             : 
    1106             :     // Copy over in case we need to use it
    1107             :     _last_intersected_extrema = _intersected_extrema;
    1108             :     // Invalidate all intersections as we're tracing again
    1109    35541892 :     _exits_elem = false;
    1110    35541892 :     _intersection_point = RayTracingCommon::invalid_point;
    1111    35541892 :     _intersected_side = RayTracingCommon::invalid_side;
    1112             :     _intersected_extrema.invalidate();
    1113    35541892 :     _intersection_distance = RayTracingCommon::invalid_distance;
    1114             : 
    1115             :     // Stationary ray
    1116    35541892 :     if (ray->stationary())
    1117             :     {
    1118             :       mooseAssert(ray->invalidDirection(), "Should have an invalid direction");
    1119        1575 :       _exits_elem = true;
    1120        1575 :       _intersection_point = _incoming_point;
    1121             :       _intersected_extrema = _last_intersected_extrema;
    1122        1575 :       _intersection_distance = 0;
    1123        1575 :       ++_results[ENDED_STATIONARY];
    1124             :     }
    1125             :     // If we haven't hit a vertex or an edge, do the normal exit algorithm first.
    1126             :     // In the case of a Ray that previously moved through point neighbors due to
    1127             :     // being at a vertex/edge, this will be true because we do not communicate the
    1128             :     // vertex/edge intersection. The previous processor already set us up for
    1129             :     // the best intersection because it already computed it and chose to
    1130             :     // send it our way as such
    1131    35540317 :     else if (!_last_intersected_extrema.atExtrema())
    1132             :     {
    1133             :       traceAssert(_current_elem->processor_id() == _pid, "Trace elem not on processor");
    1134             :       debugRay("Didn't hit vertex or edge: doing normal exits elem check");
    1135             : 
    1136    31752962 :       if (_backface_culling)
    1137      955942 :         _current_normals = _study.getElemNormals(_current_elem, _tid);
    1138             : 
    1139    31752962 :       const auto exits_elem_result = exitsElem(_current_elem,
    1140             :                                                _current_elem_type,
    1141    31752962 :                                                _incoming_side,
    1142    31752962 :                                                _intersection_point,
    1143    31752962 :                                                _intersected_side,
    1144    31752962 :                                                _intersected_extrema,
    1145    31752962 :                                                _intersection_distance,
    1146             :                                                _current_normals);
    1147             : 
    1148    31752962 :       if (exits_elem_result != NO_EXIT)
    1149             :       {
    1150    31736424 :         storeExitsElemResult(exits_elem_result);
    1151    31736424 :         _exits_elem = true;
    1152             :         ray->setCurrentPoint(_intersection_point);
    1153             :       }
    1154             :     }
    1155             :     else
    1156             :     {
    1157             :       debugRay("Will not do normal exits elem check because at a vertex/edge");
    1158             :     }
    1159             : 
    1160             :     // At this point, we either did a regular exit check and it failed, or we hit a vertex/edge
    1161             :     // on the previous intersection and didn't bother to do a regular exit check on
    1162             :     // _current_elem
    1163    35541892 :     if (!_exits_elem)
    1164             :     {
    1165             :       debugRay("Moving through neighbors");
    1166             : 
    1167             :       // The element we want moveThroughNeighbors() to try last. That is - if all others fail,
    1168             :       // this is the last resort. If we have a last intersection that tells us we're going
    1169             :       // through a vertex or edge, we probably won't go back to that elem. If we don't, it means
    1170             :       // that we failed the exitsElem() check on _current_elem and probably won't return to it
    1171             :       // either.
    1172             :       const Elem * move_through_neighbors_last = nullptr;
    1173             : 
    1174             :       // Get the neighbors
    1175             :       const std::vector<NeighborInfo> * neighbors = nullptr;
    1176             :       // If we have previous vertex/edge information, use it
    1177     3803893 :       if (_last_intersected_extrema.atExtrema())
    1178             :       {
    1179             :         traceAssert(_last_elem, "Should be valid");
    1180     3787355 :         move_through_neighbors_last = _last_elem;
    1181     3787355 :         neighbors = &getNeighbors(_last_elem, _last_intersected_extrema, _incoming_point);
    1182             :       }
    1183             :       // Without previous vertex/edge information, let's try to find some. We could just do
    1184             :       // a pure point neighbor check at the current point, but we'd rather be able to cache
    1185             :       // this information so that someone else can use it - this requires the more unique
    1186             :       // identifier of which vertex/edge we are at.
    1187             :       else
    1188             :       {
    1189             :         debugRay("  Searching for vertex/edge hit with incoming side ",
    1190             :                  _incoming_side,
    1191             :                  " on elem ",
    1192             :                  _current_elem->id(),
    1193             :                  " at ",
    1194             :                  _incoming_point);
    1195             : 
    1196       16538 :         move_through_neighbors_last = _current_elem;
    1197             : 
    1198             :         // If we have side info: check for vertices on said side, otherwise, check everywhere
    1199       16538 :         const auto at_v = _incoming_side != RayTracingCommon::invalid_side
    1200       16538 :                               ? atVertexOnSide(_current_elem, _incoming_point, _incoming_side)
    1201        1479 :                               : atVertex(_current_elem, _incoming_point);
    1202             : 
    1203       16538 :         if (at_v != RayTracingCommon::invalid_vertex)
    1204       16508 :           neighbors = &getVertexNeighbors(_current_elem, at_v);
    1205             :         // If still nothing and in 3D, check if we're on an edge
    1206             :         // TODO: Handle 2D with a similar check for if we're on a side
    1207          30 :         else if (_dim == 3)
    1208             :         {
    1209             :           ElemExtrema extrema;
    1210           0 :           if (_incoming_side != RayTracingCommon::invalid_side)
    1211           0 :             withinEdgeOnSide(_current_elem, _incoming_point, _incoming_side, extrema);
    1212             :           else
    1213           0 :             withinEdge(_current_elem, _incoming_point, extrema);
    1214             : 
    1215             :           if (extrema.atEdge())
    1216           0 :             neighbors = &getEdgeNeighbors(_current_elem, extrema, _incoming_point);
    1217             :         }
    1218             : 
    1219             :         // If we still haven't found anything - let's try a last-ditch effort
    1220       16508 :         if (!neighbors || neighbors->empty())
    1221          30 :           neighbors = &getPointNeighbors(_current_elem, _incoming_point);
    1222             : 
    1223             :         // Couldn't find anything
    1224       16538 :         if (neighbors->empty())
    1225             :         {
    1226           0 :           failTrace("Could not find neighbors to move through", _study.tolerateFailure(), __LINE__);
    1227      247985 :           return;
    1228             :         }
    1229             :       }
    1230             : 
    1231             :       // Move through a neighbor
    1232     3803893 :       const Elem * best_neighbor = nullptr;
    1233     3803893 :       auto best_neighbor_side = RayTracingCommon::invalid_side;
    1234     3803893 :       const auto exits_elem_result = moveThroughNeighbors(
    1235             :           *neighbors, move_through_neighbors_last, best_neighbor, best_neighbor_side);
    1236             : 
    1237             :       // If we didn't find anything... we're out of luck
    1238     3803893 :       if (exits_elem_result == NO_EXIT)
    1239             :       {
    1240           0 :         failTrace("Could not find intersection after trying to move through point neighbors",
    1241           0 :                   _study.tolerateFailure(),
    1242             :                   __LINE__);
    1243           0 :         return;
    1244             :       }
    1245             : 
    1246             :       // At this point, we've successfully made it through an element and also started
    1247             :       // through another element, so set that
    1248     3803893 :       _exits_elem = true;
    1249     3803893 :       _last_elem = _current_elem;
    1250     3803893 :       _current_elem = best_neighbor;
    1251     3803893 :       _incoming_side = best_neighbor_side;
    1252             :       ray->setCurrentElem(best_neighbor);
    1253             :       ray->setCurrentIncomingSide(best_neighbor_side);
    1254             :       ray->setCurrentPoint(_intersection_point);
    1255             : 
    1256             :       // Don't own this element - return exits trace for this Ray on this proc
    1257     3803893 :       if (best_neighbor->processor_id() != _pid)
    1258             :       {
    1259             :         // We've already computed the next intersection but said intersection is
    1260             :         // on an elem on another processor. Therefore, the next proc will re-trace
    1261             :         // this Ray on the perfect elem that we've picked (best_neighbor)
    1262             :         ray->setCurrentPoint(_incoming_point);
    1263      247985 :         _intersection_distance = RayTracingCommon::invalid_distance;
    1264             : 
    1265      247985 :         continueTraceOffProcessor(ray);
    1266             :         return;
    1267             :       }
    1268             : 
    1269             :       // Subdomain changed
    1270     3555908 :       if (_current_elem->subdomain_id() != _current_subdomain_id)
    1271         202 :         onSubdomainChanged(ray, /* same_ray = */ true);
    1272             : 
    1273             :       // Do own this element - tally the result as we're the ones tracing it
    1274     3555908 :       storeExitsElemResult(exits_elem_result);
    1275             :     }
    1276             : 
    1277             :     debugRay("Done with trace");
    1278             :     debugRay("  _exits_elem: ", _exits_elem);
    1279             :     debugRay("  _intersection_point: ", _intersection_point);
    1280             :     debugRay("  _intersected_side: ", _intersected_side);
    1281             :     debugRay("  _intersected_side centroid: ",
    1282             :              _intersected_side == RayTracingCommon::invalid_side
    1283             :                  ? RayTracingCommon::invalid_point
    1284             :                  : _current_elem->side_ptr(_intersected_side)->vertex_average());
    1285             :     debugRay("  _intersected_extrema = ", _intersected_extrema);
    1286             :     debugRay("  _intersection_distance = ", _intersection_distance);
    1287             : 
    1288             :     // Increment intersections
    1289    35293907 :     if (_intersection_distance > 0)
    1290             :     {
    1291             :       debugRay("Incrementing ray intersections by 1 to ", ray->intersections() + 1);
    1292             :       ray->addIntersection();
    1293    35292332 :       _results[INTERSECTIONS]++;
    1294             :     }
    1295             : 
    1296             :     // Increment distance
    1297             :     ray->addDistance(_intersection_distance);
    1298             :     debugRay("Incremented ray distance by ", _intersection_distance);
    1299             : 
    1300             :     // The effective max distance that the Ray should travel - minimum of the two
    1301    35303087 :     const auto max_distance = std::min(ray->maxDistance(), _study.rayMaxDistance());
    1302             :     debugRay("Max distance checks");
    1303             :     debugRay("  _study.rayMaxDistance() = ", _study.rayMaxDistance());
    1304             :     debugRay("  ray->maxDistance() = ", ray->maxDistance());
    1305             :     debugRay("  max_distance (effective) = ", max_distance);
    1306             : 
    1307             :     // At the maximum distance - distinguish between this and past the maximum
    1308             :     // distance because in this case we're close enough to the intersection point
    1309             :     // that we can keep it, its intersected side, and intersected extrema
    1310    35293907 :     if (MooseUtils::absoluteFuzzyEqual(ray->distance(), max_distance))
    1311             :     {
    1312             :       debugRay("At max distance");
    1313             : 
    1314             :       ray->setShouldContinue(false);
    1315       20048 :       _should_continue = false;
    1316             :     }
    1317             :     // Past the max distance - need to remove the additional distance we traveled,
    1318             :     // change the point and invalidate the intersection data (moves the Ray back)
    1319    35273859 :     else if (ray->distance() > max_distance)
    1320             :     {
    1321             :       debugRay("Past max distance");
    1322             : 
    1323             :       // The distance past the max distance we have traveled
    1324             :       const auto difference = ray->distance() - max_distance;
    1325             :       traceAssert(difference > 0, "Negative distance change after past_max_distance");
    1326             : 
    1327             :       debugRay("Removing distance ", difference);
    1328             :       ray->addDistance(-difference);
    1329             :       debugRay("  New ray->distance() = ", ray->distance());
    1330             : 
    1331     2693517 :       _intersection_point -= ray->direction() * difference;
    1332     2693517 :       _intersection_distance -= difference;
    1333     2693517 :       _intersected_side = RayTracingCommon::invalid_side;
    1334             :       _intersected_extrema.invalidate();
    1335             :       ray->setCurrentPoint(_intersection_point);
    1336             : 
    1337             :       ray->setShouldContinue(false);
    1338     2693517 :       _should_continue = false;
    1339             : 
    1340             :       traceAssert(_intersection_distance >= 0, "Negative _intersection_distance");
    1341             : #ifndef NDEBUG
    1342             :       if (_study.verifyTraceIntersections() && !_current_elem->contains_point(_intersection_point))
    1343             :         failTrace("Does not contain point after past max distance",
    1344             :                   /* warning = */ false,
    1345             :                   __LINE__);
    1346             : #endif
    1347             :     }
    1348             : 
    1349    35293907 :     if (!_study.currentRayKernels(_tid).empty())
    1350             :     {
    1351             :       debugRay("Calling onSegment() with");
    1352             :       debugRay("  current_elem->id() = ", _current_elem->id());
    1353             :       debugRay("  _incoming_point = ", _incoming_point);
    1354             :       debugRay("  _incoming_side = ", _incoming_side);
    1355             :       debugRay("  _intersection_point = ", _intersection_point);
    1356             :       debugRay("  _intersected_side = ", _intersected_side);
    1357             :       debugRay("  _intersected_extrema = ", _intersected_extrema);
    1358             :       debugRay("  _intersection_distance = ", _intersection_distance);
    1359    33831036 :       onSegment(ray);
    1360             : 
    1361    33831024 :       _study.postOnSegment(_tid, ray);
    1362             : 
    1363             :       // RayKernel killed a Ray or we're at the end
    1364             :       traceAssert(_should_continue == ray->shouldContinue(), "Should be the same");
    1365    33831024 :       if (!_should_continue)
    1366             :       {
    1367             :         debugRay("RayKernel killed the ray or past max distance");
    1368             :         traceAssert(!ray->trajectoryChanged(),
    1369             :                     "RayKernels should not change trajectories of Rays at end");
    1370             : 
    1371     2542122 :         onCompleteTrace(ray);
    1372     2542122 :         return;
    1373             :       }
    1374             : 
    1375             :       // RayKernel moved a Ray
    1376    31288902 :       if (ray->trajectoryChanged())
    1377             :       {
    1378             :         debugRay("RayKernel changed the Ray's trajectory");
    1379             :         debugRay("  new direction = ", ray->direction());
    1380             :         debugRay("  old incoming point = ", _incoming_point);
    1381             :         debugRay("  new incoming point = ", ray->currentPoint());
    1382             :         possiblyAddDebugRayMeshPoint(_incoming_point, ray->currentPoint());
    1383             : 
    1384         648 :         _incoming_point = ray->currentPoint();
    1385         648 :         _incoming_side = RayTracingCommon::invalid_side;
    1386             :         _intersected_extrema.invalidate();
    1387             :         ray->setCurrentIncomingSide(_incoming_side);
    1388             : 
    1389         648 :         const auto new_intersection_distance = (ray->currentPoint() - _incoming_point).norm();
    1390         648 :         ray->addDistance(-_intersection_distance + new_intersection_distance);
    1391         648 :         _intersection_distance = new_intersection_distance;
    1392             : 
    1393         648 :         onTrajectoryChanged(ray);
    1394         648 :         onContinueTrace(ray);
    1395             :         continue;
    1396         648 :       }
    1397             :       else
    1398             :         possiblyAddDebugRayMeshPoint(_incoming_point, _intersection_point);
    1399             :     }
    1400             :     else
    1401             :     {
    1402     1462871 :       _study.postOnSegment(_tid, ray);
    1403             : 
    1404     1462871 :       if (!_should_continue)
    1405             :       {
    1406             :         debugRay("Killing due to at end without RayKernels");
    1407             :         traceAssert(!ray->shouldContinue(), "Ray shouldn't continue");
    1408             : 
    1409      171441 :         onCompleteTrace(ray);
    1410      171441 :         return;
    1411             :       }
    1412             :     }
    1413             : 
    1414             :     // If at a vertex/on an edge and not on the boundary, we may actually be on the boundary,
    1415             :     // just not on a boundary side. Check for that here. If the domain is rectangular we will
    1416             :     // do a quick check against the bounding box as if we're not on the boundary of the
    1417             :     // bounding box for a rectangular problem, we can skip this.
    1418             :     // TODO: see how much the tolerance for the bounding box check can be tightened
    1419    32574837 :     if (_dim > 1 && _intersected_extrema.atExtrema() &&
    1420    37198493 :         _current_elem->neighbor_ptr(_intersected_side) &&
    1421     8233686 :         (!_is_rectangular_domain ||
    1422     4116843 :          onBoundingBoxBoundary(_study.boundingBox(),
    1423     4116843 :                                _intersection_point,
    1424             :                                _dim,
    1425     4116843 :                                LOOSE_TRACE_TOLERANCE * _study.domainMaxLength())))
    1426             :     {
    1427     1117190 :       auto boundary_side = RayTracingCommon::invalid_side;
    1428             :       ElemExtrema boundary_extrema;
    1429     1117190 :       const Elem * boundary_elem = nullptr;
    1430             : 
    1431     1117190 :       findExternalBoundarySide(boundary_side, boundary_extrema, boundary_elem);
    1432             : 
    1433     1117190 :       if (boundary_elem)
    1434             :       {
    1435             :         // At this point, the new incoming side is very difficult to find
    1436             :         // and may require some re-tracing backwards. Let's not do that -
    1437             :         // we don't need it to continue the trace. This is reason why
    1438             :         // _incoming_side is not available in RayBCs.
    1439      334841 :         _last_elem = _current_elem;
    1440      334841 :         _current_elem = boundary_elem;
    1441      334841 :         _intersected_side = boundary_side;
    1442             :         _intersected_extrema = boundary_extrema;
    1443             :         ray->setCurrentElem(_current_elem);
    1444             : 
    1445             :         debugRay("Found a neighbor boundary side with:");
    1446             :         debugRay("  _current_elem->id() = ", _current_elem->id());
    1447             :         debugRay("  _intersected_side = ", _intersected_side);
    1448             :         debugRay("  _intersected_extrema = ", _intersected_extrema);
    1449             :       }
    1450             :     }
    1451             : 
    1452             :     // The next element
    1453             :     const Elem * neighbor = nullptr;
    1454             : 
    1455             :     // Set up where to go next
    1456    32579684 :     _incoming_point = _intersection_point;
    1457             :     debugRay("Set _incoming point to ", _incoming_point);
    1458             : 
    1459             :     debugRay("Looking for neighbor on side ", _intersected_side, " of elem ", _current_elem->id());
    1460             : 
    1461             :     // Get the neighbor on that side
    1462             :     // If the mesh has active elements of the same level, just grab the neighbor directly. In
    1463             :     // this case, we can avoid a call to active() on the neighbor in getActiveNeighbor(), which
    1464             :     // can be expensive depending on caching.
    1465    32579684 :     neighbor = _study.hasSameLevelActiveElems()
    1466    32579684 :                    ? _current_elem->neighbor_ptr(_intersected_side)
    1467      189283 :                    : getActiveNeighbor(_current_elem, _intersected_side, _intersection_point);
    1468             : 
    1469             :     // Found one - not on the boundary so set the next info
    1470    32579684 :     if (neighbor)
    1471             :     {
    1472             :       traceAssert(neighbor->active(), "Inactive neighbor");
    1473             :       traceAssert(_current_elem->subdomain_id() == _current_subdomain_id,
    1474             :                   "_current_subdomain_id invalid");
    1475             : 
    1476             :       // If the mesh has active elements of the same level, don't use
    1477             :       // neighbor->which_neighbor_am_i(), which calls active() and an n_sides() virtual call
    1478    31169324 :       if (_study.hasSameLevelActiveElems())
    1479             :       {
    1480             :         // If the subdomain hasn't changed, we can guarantee n_sides is the same as
    1481             :         // _current_elem. Prefer this because neighbor->n_sides() is an expensive virtual. Even
    1482             :         // better, subdomain_id() isn't a virtual!
    1483    30993925 :         const unsigned short n_sides = neighbor->subdomain_id() == _current_subdomain_id
    1484             :                                            ? _current_elem_n_sides
    1485    30993925 :                                            : neighbor->n_sides();
    1486             :         traceAssert(n_sides == neighbor->n_sides(), "n_sides incorrect");
    1487             : 
    1488    94688113 :         for (_incoming_side = 0; _incoming_side < n_sides; ++_incoming_side)
    1489    94688113 :           if (neighbor->neighbor_ptr(_incoming_side) == _current_elem)
    1490             :             break;
    1491             :       }
    1492             :       else
    1493      175399 :         _incoming_side = neighbor->which_neighbor_am_i(_current_elem);
    1494             : 
    1495    31169324 :       _last_elem = _current_elem;
    1496    31169324 :       _current_elem = neighbor;
    1497             :       ray->setCurrentElem(neighbor);
    1498    31169324 :       ray->setCurrentIncomingSide(_incoming_side);
    1499             : 
    1500             :       debugRay("Next elem: ", neighbor->id(), " with centroid ", neighbor->vertex_average());
    1501             :       debugRay("Next _incoming_side: ",
    1502             :                _incoming_side,
    1503             :                " with centroid ",
    1504             :                neighbor->side_ptr(_incoming_side)->vertex_average());
    1505             :       traceAssert(_last_elem->subdomain_id() == _current_subdomain_id,
    1506             :                   "_current_subdomain_id invalid");
    1507             : 
    1508             :       // Whether or not the subdomain changed
    1509    31169324 :       const bool subdomain_changed = neighbor->subdomain_id() != _current_subdomain_id;
    1510             : 
    1511             :       // See if we hit any internal sides leaving this element and apply
    1512             :       // We require that all internal RayBCs be on internal sidesets that have different
    1513             :       // subdomain ids on each side. Therefore, only check if at vertex/edge or if
    1514             :       // the subdomain changes
    1515    31169324 :       if (_study.hasInternalSidesets() && (subdomain_changed || _intersected_extrema.atExtrema()))
    1516             :       {
    1517        2506 :         applyOnInternalBoundary(ray);
    1518             : 
    1519             :         // Internal RayBC killed a Ray
    1520        2502 :         if (!_should_continue)
    1521             :         {
    1522             :           traceAssert(!ray->shouldContinue(), "Should be the same");
    1523             :           debugRay("Internal RayBC killed the ray");
    1524             : 
    1525         234 :           onCompleteTrace(ray);
    1526         234 :           return;
    1527             :         }
    1528             : 
    1529             :         // Internal RayBC changed the Ray
    1530        2268 :         if (ray->trajectoryChanged())
    1531             :         {
    1532             :           debugRay("Internal RayBC changed the trajectory:");
    1533             :           debugRay("  new direction = ", ray->direction());
    1534             : 
    1535             :           // Is this side still incoming?
    1536         441 :           const auto normal = _study.getSideNormal(_current_elem, _incoming_side, _tid);
    1537             :           const auto dot = normal * ray->direction();
    1538             :           debugRay("Dot product with new direction and side = ", dot);
    1539         441 :           if (dot > -TRACE_TOLERANCE)
    1540             :           {
    1541         405 :             _incoming_side = _last_elem->which_neighbor_am_i(_current_elem);
    1542         405 :             _current_elem = _last_elem;
    1543             :             neighbor = _last_elem;
    1544             :             ray->setCurrentElem(_current_elem);
    1545             :             ray->setCurrentIncomingSide(_incoming_side);
    1546             :             debugRay("  Dot > 0 (Ray turned around): Setting _current_elem = ",
    1547             :                      _current_elem->id(),
    1548             :                      " and _incoming_side = ",
    1549             :                      _incoming_side);
    1550             :           }
    1551             : 
    1552         441 :           onTrajectoryChanged(ray);
    1553             :         }
    1554             : 
    1555             :         traceAssert(ray->currentPoint().absolute_fuzzy_equals(_intersection_point, TRACE_TOLERANCE),
    1556             :                     "Internal RayBC changed the Ray point");
    1557             :       }
    1558             : 
    1559             :       // Neighbor is off processor
    1560             :       // If we hit at a vertex/edge, we will continue and let the move through neighbor exit
    1561             :       // figure out who to send to
    1562    31169086 :       if (neighbor->processor_id() != _pid)
    1563             :       {
    1564     1156350 :         if (_intersected_extrema.atExtrema())
    1565             :         {
    1566             :           debugRay("Neighbor is off processor but continuing to move through neighbors");
    1567             :         }
    1568             :         else
    1569             :         {
    1570     1011341 :           continueTraceOffProcessor(ray);
    1571     1011341 :           return;
    1572             :         }
    1573             :       }
    1574             : 
    1575             :       // Neighbor is on processor, call subdomain setup if needed
    1576    30157745 :       if (subdomain_changed)
    1577        2422 :         onSubdomainChanged(ray, /* same_ray = */ true);
    1578             :     }
    1579             :     // No neighbor found: on the boundary
    1580             :     else
    1581             :     {
    1582             :       debugRay("No neighbor found - on the boundary");
    1583             : 
    1584             :       // Apply boundary conditions
    1585     1410360 :       applyOnExternalBoundary(ray);
    1586             : 
    1587             :       traceAssert(ray->currentPoint().absolute_fuzzy_equals(_intersection_point, TRACE_TOLERANCE),
    1588             :                   "RayBC changed the Ray point");
    1589             : 
    1590             :       // Quit tracing if the Ray was killed by a BC
    1591     1410350 :       if (!_should_continue)
    1592             :       {
    1593             :         traceAssert(!ray->shouldContinue(), "Should be the same");
    1594             :         debugRay("Exiting due to death by BC");
    1595             : 
    1596     1407047 :         onCompleteTrace(ray);
    1597     1407047 :         return;
    1598             :       }
    1599             :       // RayBC changed the direction of the Ray
    1600        3303 :       if (ray->trajectoryChanged())
    1601             :       {
    1602             :         debugRay("RayBC changed the trajectory");
    1603             :         debugRay("  new direction = ", ray->direction());
    1604             :         traceAssert(ray->direction() *
    1605             :                             _study.getSideNormal(_current_elem, _intersected_side, _tid) <
    1606             :                         TRACE_TOLERANCE,
    1607             :                     "Reflected ray is not incoming");
    1608             :         possiblyAddDebugRayMeshPoint(_incoming_point, _intersection_point);
    1609             : 
    1610        3303 :         _last_elem = _current_elem;
    1611        3303 :         _incoming_point = _intersection_point;
    1612        3303 :         _incoming_side = _intersected_side;
    1613             :         ray->setCurrentPoint(_incoming_point);
    1614             :         ray->setCurrentIncomingSide(_incoming_side);
    1615        3303 :         onTrajectoryChanged(ray);
    1616             :       }
    1617             :     }
    1618             : 
    1619    30161048 :     onContinueTrace(ray);
    1620             :   } while (true);
    1621             : 
    1622             :   // If a trace made its way down here and didn't return... it failed
    1623             :   failTrace("Could not find an intersection", _study.tolerateFailure(), __LINE__);
    1624             : }
    1625             : 
    1626             : void
    1627     4120844 : TraceRay::onCompleteTrace(const std::shared_ptr<Ray> & ray)
    1628             : {
    1629     7916772 :   for (RayKernelBase * rk : _study.currentRayKernels(_tid))
    1630     3795928 :     rk->postTrace();
    1631             : 
    1632             :   debugRay("Called onCompleteTrace()\n", (*_current_ray)->getInfo());
    1633             :   if (_intersection_distance > 0)
    1634             :     possiblyAddDebugRayMeshPoint(_incoming_point, _intersection_point);
    1635             :   possiblySaveDebugRayMesh();
    1636             : 
    1637     4120844 :   if (_current_cached_trace)
    1638             :   {
    1639       11392 :     _current_cached_trace->_last = true;
    1640             : 
    1641       11392 :     if (_intersection_distance > 0)
    1642             :     {
    1643             :       _current_cached_trace->addPoint(ray->currentPoint());
    1644       10285 :       if (_study.dataOnCacheTraces())
    1645         170 :         _current_cached_trace->lastPoint()._data = ray->data();
    1646       10285 :       if (_study.auxDataOnCacheTraces())
    1647         134 :         _current_cached_trace->lastPoint()._aux_data = ray->auxData();
    1648             :     }
    1649             : 
    1650             :     mooseAssert(ray->stationary() == _current_cached_trace->stationary(), "Stationary mismatch");
    1651             :   }
    1652     4120844 : }
    1653             : 
    1654             : void
    1655    30161696 : TraceRay::onContinueTrace(const std::shared_ptr<Ray> & ray)
    1656             : {
    1657             :   traceAssert(ray->shouldContinue(), "Ray must continue");
    1658             : 
    1659    30161696 :   if (_current_cached_trace && _study.segmentsOnCacheTraces() && _intersection_distance > 0)
    1660             :   {
    1661             :     _current_cached_trace->addPoint(ray->currentPoint());
    1662       45860 :     if (_study.dataOnCacheTraces())
    1663         651 :       _current_cached_trace->lastPoint()._data = ray->data();
    1664       45860 :     if (_study.auxDataOnCacheTraces())
    1665         507 :       _current_cached_trace->lastPoint()._aux_data = ray->auxData();
    1666             :   }
    1667    30161696 : }
    1668             : 
    1669             : void
    1670     1259326 : TraceRay::continueTraceOffProcessor(const std::shared_ptr<Ray> & ray)
    1671             : {
    1672             :   traceAssert(ray->currentElem() == _current_elem, "Ray currentElem() invalid");
    1673             :   traceAssert(ray->currentIncomingSide() == _incoming_side, "Ray currentIncomingSide() invalid");
    1674             :   traceAssert(ray->currentPoint() == _incoming_point, "Ray currentPoint() invalid");
    1675             :   traceAssert(_current_elem->processor_id() != _pid, "Off processor trace is not off processor");
    1676             :   debugRay("Ray going off processor to ", _current_elem->processor_id());
    1677             : 
    1678             :   ray->addProcessorCrossing();
    1679             : 
    1680     1259326 :   if (_current_cached_trace && _intersection_distance > 0)
    1681             :   {
    1682        2194 :     _current_cached_trace->addPoint(_incoming_point);
    1683        2194 :     if (_study.dataOnCacheTraces())
    1684          21 :       _current_cached_trace->lastPoint()._data = ray->data();
    1685        2194 :     if (_study.auxDataOnCacheTraces())
    1686          21 :       _current_cached_trace->lastPoint()._aux_data = ray->auxData();
    1687             :   }
    1688             : 
    1689             :   if (_intersection_distance > 0)
    1690             :     possiblyAddDebugRayMeshPoint(_incoming_point, _intersection_point);
    1691             :   possiblySaveDebugRayMesh();
    1692     1259326 : }
    1693             : 
    1694             : void
    1695        4392 : TraceRay::onTrajectoryChanged(const std::shared_ptr<Ray> & ray)
    1696             : {
    1697             : #ifndef NDEBUG
    1698             :   if (_study.verifyTraceIntersections() &&
    1699             :       (_intersected_extrema.atExtrema()
    1700             :            ? !_current_elem->close_to_point(ray->currentPoint(), LOOSE_TRACE_TOLERANCE)
    1701             :            : !_current_elem->contains_point(ray->currentPoint())))
    1702             :     failTrace("Elem does not contain point after trajectory change",
    1703             :               /* warning = */ false,
    1704             :               __LINE__);
    1705             : #endif
    1706             : 
    1707             :   traceAssert(ray->shouldContinue(), "Ray should continue when trajectory is being changed");
    1708             : 
    1709             :   ray->setTrajectoryChanged(false);
    1710             :   ray->addTrajectoryChange();
    1711             : 
    1712        4392 :   if (_current_cached_trace && !_study.segmentsOnCacheTraces())
    1713             :   {
    1714          48 :     if (_intersection_distance > 0)
    1715             :       _current_cached_trace->addPoint(ray->currentPoint());
    1716          48 :     if (_study.dataOnCacheTraces())
    1717           0 :       _current_cached_trace->lastPoint()._data = ray->data();
    1718          48 :     if (_study.auxDataOnCacheTraces())
    1719           0 :       _current_cached_trace->lastPoint()._aux_data = ray->auxData();
    1720             :   }
    1721        4392 : }
    1722             : 
    1723             : void
    1724     5382820 : TraceRay::onSubdomainChanged(const std::shared_ptr<Ray> & ray, const bool same_ray)
    1725             : {
    1726             :   debugRay("Calling onSubdomainChanged() on subdomain ", _current_elem->subdomain_id());
    1727             :   debugRay("  _current_subdomain_id = ", _current_subdomain_id);
    1728             : 
    1729     5382820 :   _current_subdomain_id = _current_elem->subdomain_id();
    1730     5382820 :   _current_subdomain_hmax = _study.subdomainHmax(_current_subdomain_id);
    1731     5382820 :   _current_elem_type = _current_elem->type();
    1732     5382820 :   _current_elem_n_sides = _current_elem->n_sides();
    1733             : 
    1734     5382820 :   if (_has_ray_kernels)
    1735             :   {
    1736     4955206 :     auto & current_ray_kernels = _study.currentRayKernels(_tid);
    1737             : 
    1738             :     // If we're still tracing the same Ray, keep track of our old RayKernels
    1739             :     // so that we don't call preTrace() on them again
    1740     4955206 :     if (same_ray)
    1741        1650 :       _old_ray_kernels.insert(current_ray_kernels.begin(), current_ray_kernels.end());
    1742             :     // If we're not tracing the same Ray, we need to call preTrace() on everything
    1743             :     else
    1744             :       _old_ray_kernels.clear();
    1745             : 
    1746             :     // Call segmentSubdomainSetup to get new kernels etc
    1747     4955206 :     _study.segmentSubdomainSetup(_current_subdomain_id, _tid, ray->id());
    1748             : 
    1749             :     // Call preTrace() on all of the RayKernels that need it
    1750     9917397 :     for (RayKernelBase * rk : current_ray_kernels)
    1751             :       // Haven't called preTrace() for this Ray on this RayKernel yet
    1752             :       if (!_old_ray_kernels.count(rk))
    1753     4962123 :         rk->preTrace();
    1754             :   }
    1755     5382820 : }
    1756             : 
    1757             : std::string
    1758           9 : TraceRay::failTraceMessage(const std::string & reason, const int line)
    1759             : {
    1760           9 :   std::stringstream oss;
    1761           9 :   oss << "Ray on processor " << _pid << " and thread " << _tid << " failed to trace";
    1762           9 :   if (line != -1)
    1763           9 :     oss << " at line " << line;
    1764          18 :   oss << "\n\n" << reason << "\n\n";
    1765          18 :   oss << ((*_current_ray))->getInfo() << "\n";
    1766           9 :   oss << "Current trace information\n";
    1767           9 :   oss << "  _current_subdomain_id = ";
    1768           9 :   if (_current_subdomain_id == Elem::invalid_subdomain_id)
    1769           0 :     oss << "invalid subdomain id\n";
    1770             :   else
    1771           9 :     oss << _current_subdomain_id << "\n";
    1772          18 :   oss << "  _current_elem_type = " << Utility::enum_to_string(_current_elem_type) << "\n";
    1773           9 :   oss << "  _current_elem_n_sides = " << _current_elem_n_sides << "\n";
    1774           9 :   oss << "  _incoming_point = ";
    1775             :   if (_incoming_point == RayTracingCommon::invalid_point)
    1776           0 :     oss << "invalid point\n";
    1777             :   else
    1778           9 :     oss << _incoming_point << "\n";
    1779           9 :   oss << "  _incoming_side = ";
    1780           9 :   if (_incoming_side == RayTracingCommon::invalid_side)
    1781           0 :     oss << "invalid side\n";
    1782             :   else
    1783           9 :     oss << _incoming_side << "\n";
    1784           9 :   oss << "  _intersection_point = ";
    1785             :   if (_intersection_point == RayTracingCommon::invalid_point)
    1786           0 :     oss << "invalid point\n";
    1787             :   else
    1788           9 :     oss << _intersection_point << "\n";
    1789           9 :   oss << "  _intersected_side = ";
    1790           9 :   if (_intersected_side == RayTracingCommon::invalid_side)
    1791           0 :     oss << "invalid side\n";
    1792             :   else
    1793           9 :     oss << _intersected_side << "\n";
    1794           9 :   oss << "  _intersected_extrema = " << _intersected_extrema << "\n";
    1795           9 :   oss << "  _exits_elem = " << _exits_elem << "\n";
    1796           9 :   if (_current_elem)
    1797          18 :     oss << _current_elem->get_info();
    1798             :   else
    1799           0 :     oss << "_current_elem = invalid\n";
    1800             : 
    1801             :   possiblySaveDebugRayMesh();
    1802           9 :   return oss.str();
    1803           9 : }
    1804             : 
    1805             : void
    1806           9 : TraceRay::failTrace(const std::string & reason, const bool warning, const int line)
    1807             : {
    1808           9 :   const auto message = failTraceMessage(reason, line);
    1809             : 
    1810           9 :   if (warning)
    1811             :   {
    1812           5 :     ++_results[FAILED_TRACES];
    1813             :     mooseWarning(message);
    1814           5 :     (*_current_ray)->setShouldContinue(false);
    1815           5 :     _should_continue = false;
    1816             :   }
    1817             :   else
    1818           4 :     mooseError(message);
    1819           5 : }
    1820             : 
    1821             : const std::vector<NeighborInfo> &
    1822     1261716 : TraceRay::getVertexNeighbors(const Elem * elem, const Node * vertex)
    1823             : {
    1824             :   traceAssert(elem, "Elem must be valid");
    1825             :   traceAssert(vertex, "Vertex must be valid");
    1826             : 
    1827             :   debugRay("Called getVertexNeighbors() with:");
    1828             :   debugRay("  elem->id() = ", elem->id(), " with centroid ", elem->vertex_average());
    1829             :   debugRay("  vertex->id() = ", vertex->id(), ", at ", (Point)*vertex);
    1830             : 
    1831             :   traceAssert(elem->get_node_index(vertex) != libMesh::invalid_uint, "Doesn't contain node");
    1832             :   traceAssert(elem->is_vertex(elem->get_node_index(vertex)), "Node is not a vertex");
    1833             : 
    1834     1261716 :   ++_results[VERTEX_NEIGHBOR_LOOKUPS];
    1835             : 
    1836             :   // Return the entry if we have it cached
    1837             :   auto search = _vertex_neighbors.find(vertex);
    1838     1261716 :   if (search != _vertex_neighbors.end())
    1839     1155977 :     return search->second;
    1840             : 
    1841      105739 :   ++_results[VERTEX_NEIGHBOR_BUILDS];
    1842             : 
    1843             :   // Make a new entry
    1844             :   debugRay("Building vertex neighbors");
    1845             :   std::vector<NeighborInfo> & entry =
    1846      105739 :       _vertex_neighbors.emplace(vertex, std::vector<NeighborInfo>()).first->second;
    1847             : 
    1848      105739 :   findNodeNeighbors(elem,
    1849             :                     vertex,
    1850      105739 :                     _neighbor_set,
    1851      105739 :                     _neighbor_untested_set,
    1852      105739 :                     _neighbor_next_untested_set,
    1853      105739 :                     _neighbor_active_neighbor_children,
    1854             :                     entry);
    1855             : 
    1856             :   // Fill the side normals
    1857      815937 :   for (auto & neighbor_info : entry)
    1858     2847464 :     for (MooseIndex(neighbor_info._sides.size()) i = 0; i < neighbor_info._sides.size(); ++i)
    1859     2137266 :       neighbor_info._side_normals[i] =
    1860     2137266 :           _study.getSideNormal(neighbor_info._elem, neighbor_info._sides[i], _tid);
    1861             : 
    1862             :   return entry;
    1863             : }
    1864             : 
    1865             : const std::vector<NeighborInfo> &
    1866     1261716 : TraceRay::getVertexNeighbors(const Elem * elem, const unsigned short vertex)
    1867             : {
    1868             :   traceAssert(vertex < elem->n_vertices(), "Invalid vertex");
    1869             : 
    1870     1261716 :   return getVertexNeighbors(elem, elem->node_ptr(vertex));
    1871             : }
    1872             : 
    1873             : const std::vector<NeighborInfo> &
    1874     4325854 : TraceRay::getEdgeNeighbors(const Elem * elem,
    1875             :                            const std::pair<const Node *, const Node *> & vertices,
    1876             :                            const Point & point)
    1877             : {
    1878             :   traceAssert(elem, "Invalid elem");
    1879             :   traceAssert(vertices.first, "Must be valid");
    1880             :   traceAssert(vertices.second, "Must be valid");
    1881             : 
    1882             :   debugRay("Called getEdgeNeighbors() with:");
    1883             :   debugRay("  elem->id() = ", elem->id(), " with centroid ", elem->vertex_average());
    1884             :   debugRay("  vertices.first = ", vertices.first->id(), " at ", (Point)*vertices.first);
    1885             :   debugRay("  vertices.second = ", vertices.second->id(), " at ", (Point)*vertices.second);
    1886             :   debugRay("  point = ", point);
    1887             : 
    1888             :   traceAssert(elem->get_node_index(vertices.first) != libMesh::invalid_uint,
    1889             :               "Doesn't contain vertex");
    1890             :   traceAssert(elem->get_node_index(vertices.second) != libMesh::invalid_uint,
    1891             :               "Doesn't contain vertex");
    1892             :   traceAssert(isWithinSegment(
    1893             :                   (Point)*vertices.first, (Point)*vertices.second, point, LOOSE_TRACE_TOLERANCE),
    1894             :               "Point not within edge");
    1895             : 
    1896     4325854 :   ++_results[EDGE_NEIGHBOR_LOOKUPS];
    1897             : 
    1898     4325854 :   const auto ordered_vertices = vertices.first->id() < vertices.second->id()
    1899     4325854 :                                     ? vertices
    1900     4325854 :                                     : std::make_pair(vertices.second, vertices.first);
    1901             : 
    1902             :   // Look for the entry and build if necessary
    1903             :   std::pair<bool, std::vector<NeighborInfo>> * entry;
    1904             :   auto search = _edge_neighbors.find(ordered_vertices);
    1905     4325854 :   if (search != _edge_neighbors.end())
    1906     4114249 :     entry = &search->second;
    1907             :   else
    1908             :   {
    1909             :     debugRay("Building edge neighbors");
    1910      211605 :     ++_results[EDGE_NEIGHBOR_BUILDS];
    1911      211605 :     entry = &_edge_neighbors
    1912      211605 :                  .emplace(ordered_vertices, std::make_pair(true, std::vector<NeighborInfo>()))
    1913             :                  .first->second;
    1914      211605 :     findEdgeNeighbors(elem,
    1915      211605 :                       ordered_vertices.first,
    1916      211605 :                       ordered_vertices.second,
    1917      211605 :                       _neighbor_set,
    1918      211605 :                       _neighbor_untested_set,
    1919      211605 :                       _neighbor_next_untested_set,
    1920      211605 :                       _neighbor_active_neighbor_children,
    1921      211605 :                       entry->second);
    1922             : 
    1923             :     bool all_same_edge = true;
    1924     1042460 :     for (auto & neighbor_info : entry->second)
    1925             :     {
    1926             :       traceAssert(neighbor_info._lower_bound <= neighbor_info._upper_bound,
    1927             :                   "Bound order incorrect");
    1928             : 
    1929             :       // Fill the side normals
    1930     2492501 :       for (MooseIndex(neighbor_info._sides.size()) i = 0; i < neighbor_info._sides.size(); ++i)
    1931     1661646 :         neighbor_info._side_normals[i] =
    1932     1661646 :             _study.getSideNormal(neighbor_info._elem, neighbor_info._sides[i], _tid);
    1933             : 
    1934             :       // See if the bounds are the same as the target edge
    1935      830855 :       if (neighbor_info._lower_bound != 0 || neighbor_info._upper_bound != 1)
    1936             :         all_same_edge = false;
    1937             :     }
    1938      211605 :     entry->first = all_same_edge;
    1939             :   }
    1940             : 
    1941             :   // Means that all neighbors are not on the exact same edge, so we must
    1942             :   // validate/invalidate based on if the neighbor's edge contains our point
    1943     4325854 :   if (!entry->first)
    1944             :   {
    1945             :     const auto edge_length =
    1946         892 :         ((Point)*ordered_vertices.first - (Point)*ordered_vertices.second).norm();
    1947         892 :     const auto point_location = ((Point)*ordered_vertices.first - point).norm() / edge_length;
    1948        6410 :     for (auto & info : entry->second)
    1949       11036 :       info._valid = (info._lower_bound - TRACE_TOLERANCE) < point_location &&
    1950        4600 :                     point_location < (info._upper_bound + TRACE_TOLERANCE);
    1951             :   }
    1952             : 
    1953     4325854 :   return entry->second;
    1954             : }
    1955             : 
    1956             : const std::vector<NeighborInfo> &
    1957     4325854 : TraceRay::getEdgeNeighbors(const Elem * elem,
    1958             :                            const std::pair<unsigned short, unsigned short> & vertices,
    1959             :                            const Point & point)
    1960             : {
    1961             :   debugRay("Called getEdgeNeighbors(), local index version with:");
    1962             :   debugRay("  vertices.first = ", vertices.first);
    1963             :   debugRay("  vertices.second = ", vertices.second);
    1964             :   traceAssert(vertices.first < elem->n_vertices(),
    1965             :               "Invalid vertex with ray " + std::to_string((*_current_ray)->id()));
    1966             :   traceAssert(vertices.second < elem->n_vertices(), "Invalid vertex");
    1967             : 
    1968     4325854 :   return getEdgeNeighbors(
    1969     4325854 :       elem, std::make_pair(elem->node_ptr(vertices.first), elem->node_ptr(vertices.second)), point);
    1970             : }
    1971             : 
    1972             : const std::vector<NeighborInfo> &
    1973     5571062 : TraceRay::getNeighbors(const Elem * elem, const ElemExtrema & extrema, const Point & point)
    1974             : {
    1975     5571062 :   if (!extrema.atExtrema())
    1976           0 :     return getPointNeighbors(elem, point);
    1977             :   if (extrema.atVertex())
    1978     1245208 :     return getVertexNeighbors(elem, extrema.vertex());
    1979     4325854 :   return getEdgeNeighbors(elem, extrema.edgeVertices(), point);
    1980             : }
    1981             : 
    1982             : const std::vector<NeighborInfo> &
    1983          30 : TraceRay::getPointNeighbors(const Elem * elem, const Point & point)
    1984             : {
    1985             :   traceAssert(elem, "Invalid elem");
    1986             : 
    1987             :   debugRay("Called getPointNeighbors()");
    1988             :   debugRay(" elem = ", elem->id());
    1989             :   debugRay(" point = ", point);
    1990             : 
    1991          30 :   ++_results[POINT_NEIGHBOR_BUILDS];
    1992          30 :   _point_neighbor_helper.clear();
    1993             : 
    1994          30 :   findPointNeighbors(elem,
    1995             :                      point,
    1996          30 :                      _neighbor_set,
    1997          30 :                      _neighbor_untested_set,
    1998          30 :                      _neighbor_next_untested_set,
    1999          30 :                      _neighbor_active_neighbor_children,
    2000             :                      _point_neighbor_helper);
    2001             : 
    2002             :   // Fill the side normals
    2003          60 :   for (auto & neighbor_info : _point_neighbor_helper)
    2004          60 :     for (MooseIndex(neighbor_info._sides.size()) i = 0; i < neighbor_info._sides.size(); ++i)
    2005          30 :       neighbor_info._side_normals[i] =
    2006          30 :           _study.getSideNormal(neighbor_info._elem, neighbor_info._sides[i], _tid);
    2007             : 
    2008          30 :   return _point_neighbor_helper;
    2009             : }
    2010             : 
    2011             : void
    2012    35292332 : TraceRay::storeExitsElemResult(const TraceRay::ExitsElemResult result)
    2013             : {
    2014    35292332 :   if (result == HIT_FACE)
    2015    30505295 :     ++_results[FACE_HITS];
    2016     4787037 :   else if (result == HIT_VERTEX)
    2017     1108104 :     ++_results[VERTEX_HITS];
    2018     3678933 :   else if (result == HIT_EDGE)
    2019     3678933 :     ++_results[EDGE_HITS];
    2020             :   else
    2021           0 :     mooseError("Should not call storeExitsElemResult() with result ", result);
    2022    35292332 : }
    2023             : 
    2024             : void
    2025    33831036 : TraceRay::onSegment(const std::shared_ptr<Ray> & ray)
    2026             : {
    2027             :   traceAssert((*_current_ray)->currentElem() == _current_elem, "Ray currentElem() incorrect");
    2028             :   traceAssert((*_current_ray)->currentPoint() == _intersection_point,
    2029             :               "Ray currentPoint() incorrect");
    2030             :   traceAssert((*_current_ray)->currentIncomingSide() == _incoming_side,
    2031             :               "Ray currentIncomingSide() incorrect");
    2032             : #ifndef NDEBUG
    2033             :   if (_study.verifyTraceIntersections())
    2034             :   {
    2035             :     if (_current_elem->has_affine_map())
    2036             :       traceAssert(_current_elem->contains_point(_incoming_point),
    2037             :                   "_current_elem does not contain incoming point");
    2038             : 
    2039             :     if (_intersected_side != RayTracingCommon::invalid_side &&
    2040             :         !_study.sideIsNonPlanar(_current_elem, _intersected_side))
    2041             :     {
    2042             :       traceAssert(_elem_side_builder(*_current_elem, _intersected_side)
    2043             :                       .close_to_point(_intersection_point, LOOSE_TRACE_TOLERANCE),
    2044             :                   "Intersected point is not on intersected side");
    2045             :       traceAssert(!_study.sideIsIncoming(
    2046             :                       _current_elem, _intersected_side, (*_current_ray)->direction(), _tid),
    2047             :                   "Intersected side is not outgoing");
    2048             :     }
    2049             :     if (_incoming_side != RayTracingCommon::invalid_side &&
    2050             :         !_study.sideIsNonPlanar(_current_elem, _incoming_side))
    2051             :     {
    2052             :       traceAssert(_elem_side_builder(*_current_elem, _incoming_side)
    2053             :                       .close_to_point(_incoming_point, LOOSE_TRACE_TOLERANCE),
    2054             :                   "Incoming point is not on incoming side");
    2055             :       if (ray->intersections() != 0 && ray->maxDistance() != 0)
    2056             :         traceAssert(_study.sideIsIncoming(
    2057             :                         _current_elem, _incoming_side, (*_current_ray)->direction(), _tid),
    2058             :                     "Incoming side is not incoming");
    2059             :     }
    2060             :   }
    2061             : #endif
    2062             :   traceAssert(MooseUtils::absoluteFuzzyEqual(_intersection_distance,
    2063             :                                              (_intersection_point - _incoming_point).norm()),
    2064             :               "_intersection_distance is incorrect");
    2065             :   traceAssert(_current_subdomain_id == _current_elem->subdomain_id(), "Subdomain incorrect");
    2066             :   traceAssert(MooseUtils::absoluteFuzzyEqual((_incoming_point - _intersection_point).norm(),
    2067             :                                              _intersection_distance),
    2068             :               "Invalid intersection distance");
    2069             : 
    2070    33831036 :   _study.reinitSegment(
    2071    33831036 :       _current_elem, _incoming_point, _intersection_point, _intersection_distance, _tid);
    2072             : 
    2073    33831036 :   const auto & rks = _study.currentRayKernels(_tid);
    2074    67676133 :   for (auto & rk : rks)
    2075             :   {
    2076    33845109 :     rk->onSegment();
    2077    33845097 :     postRayTracingObject(ray, rk);
    2078             :   }
    2079    33831024 : }
    2080             : 
    2081             : void
    2082     1411579 : TraceRay::onBoundary(const std::shared_ptr<Ray> & ray, const bool external)
    2083             : {
    2084             :   traceAssert(ray->currentPoint().absolute_fuzzy_equals(_intersection_point),
    2085             :               "Ray currentPoint() not set before onBoundary()");
    2086             : 
    2087             :   // Get the RayBCs on bnd_elems
    2088     1411579 :   _study.getRayBCs(_on_boundary_ray_bcs, _boundary_elems, _tid, ray->id());
    2089             : 
    2090             :   // Store this information temprorarily because we are going to change it as we
    2091             :   // apply each boundary condition
    2092     1411579 :   const auto old_current_elem = _current_elem;
    2093     1411579 :   const auto old_intersected_side = _intersected_side;
    2094     1411579 :   const auto old_intersected_extrema = _intersected_extrema;
    2095     1411579 :   const auto old_subdomain_id = _current_subdomain_id;
    2096             : 
    2097             :   // For each RayBC we found, apply it on the boundaries that we need
    2098     2847774 :   for (RayBoundaryConditionBase * rbc : _on_boundary_ray_bcs)
    2099             :   {
    2100             :     // First, find the boundaries this RayBC is valid on that are also in _boundary_elems.
    2101             :     // We do this ahead of time so that we can pass in to RayBC::apply if the same
    2102             :     // boundary condition is being appled multiple times on different boundaries.
    2103             :     // This is useful in situations like reflection where multiple reflections are
    2104             :     // necessary at a corner to perfectly reflect.
    2105     1436205 :     _on_boundary_apply_index.clear();
    2106     2974449 :     for (MooseIndex(_boundary_elems.size()) bnd_elems_i = 0; bnd_elems_i < _boundary_elems.size();
    2107             :          ++bnd_elems_i)
    2108     1538244 :       if (rbc->hasBoundary(_boundary_elems[bnd_elems_i].bnd_id))
    2109     1538170 :         _on_boundary_apply_index.push_back(bnd_elems_i);
    2110             : 
    2111             :     traceAssert(!_on_boundary_apply_index.empty(), "Must not be empty");
    2112             : 
    2113             :     // Apply the RayBC on each of the relevant boundary elements
    2114     2974365 :     for (const auto bnd_elems_i : _on_boundary_apply_index)
    2115             :     {
    2116             :       auto & bnd_elem = _boundary_elems[bnd_elems_i];
    2117     1538170 :       _current_elem = bnd_elem.elem;
    2118     1538170 :       _current_bnd_id = bnd_elem.bnd_id;
    2119     1538170 :       _intersected_side = bnd_elem.side;
    2120             :       _intersected_extrema = bnd_elem.extrema;
    2121     1538170 :       _current_subdomain_id = _current_elem->subdomain_id();
    2122             : 
    2123     1538170 :       rbc->onBoundary(_on_boundary_apply_index.size());
    2124     1538160 :       postRayTracingObject(ray, rbc);
    2125             :     }
    2126             :   }
    2127             : 
    2128             :   // Set this info back now that we're done applying BCs
    2129     1411569 :   _current_elem = old_current_elem;
    2130     1411569 :   _intersected_side = old_intersected_side;
    2131             :   _intersected_extrema = old_intersected_extrema;
    2132     1411569 :   _current_bnd_id = BoundaryInfo::invalid_id;
    2133     1411569 :   _current_subdomain_id = old_subdomain_id;
    2134             : 
    2135             :   // When on an external boundary, the Ray must have been changed or killed.
    2136             :   // Otherwise, we don't know what to do with it now! If this didn't happen,
    2137             :   // output a detailed error message.
    2138     1411569 :   if (external && !ray->trajectoryChanged() && ray->shouldContinue())
    2139             :   {
    2140           9 :     std::stringstream oss;
    2141           9 :     oss << "Don't know what to do with a Ray after it hit an external\n";
    2142           9 :     oss << "boundary at point " << _intersection_point << "!\n\n";
    2143           9 :     oss << "When hitting an external RayBC, a Ray must either:\n";
    2144           9 :     oss << "  Be killed by a RayBC\n";
    2145           9 :     oss << "  Have its trajectory changed by the RayBC\n";
    2146           9 :     oss << "by at least one of the executed RayBCs.\n\n";
    2147           9 :     oss << "You need to either:\n";
    2148           9 :     oss << "  Kill/change the Ray sooner with RayKernels, internal RayBCs, or a max distance\n";
    2149           9 :     oss << "  Kill/change the Ray on the boundary with a RayBC\n\n";
    2150           9 :     if (!_on_boundary_ray_bcs.empty())
    2151             :     {
    2152           7 :       oss << "RayBCs executed that did not kill or change the Ray:\n";
    2153          14 :       for (const RayBoundaryConditionBase * rbc : _on_boundary_ray_bcs)
    2154          16 :         for (const auto & bnd_elem : _boundary_elems)
    2155           9 :           if (rbc->hasBoundary(bnd_elem.bnd_id))
    2156          21 :             oss << "  " << rbc->typeAndName() << " on boundary " << bnd_elem.bnd_id << " ("
    2157          14 :                 << _mesh.getBoundaryName(bnd_elem.bnd_id) << ")\n";
    2158           7 :       oss << "\n";
    2159             :     }
    2160             :     bool output_header = false;
    2161          22 :     for (std::size_t i = 0; i < _boundary_elems.size(); ++i)
    2162             :     {
    2163          13 :       const auto bnd_id = _boundary_elems[i].bnd_id;
    2164             :       bool found = false;
    2165          15 :       for (const RayBoundaryConditionBase * rbc : _on_boundary_ray_bcs)
    2166           9 :         if (rbc->hasBoundary(bnd_id))
    2167             :         {
    2168             :           found = true;
    2169             :           break;
    2170             :         }
    2171             : 
    2172          13 :       if (!found)
    2173             :       {
    2174           6 :         if (!output_header)
    2175             :         {
    2176           4 :           oss << "Boundaries that did not have any RayBCs:\n";
    2177             :           output_header = true;
    2178             :         }
    2179          12 :         oss << "  " << bnd_id << " (" << _mesh.getBoundaryName(bnd_id) << ")\n";
    2180             :       }
    2181             :     }
    2182             : 
    2183           9 :     failTrace(oss.str(), _study.tolerateFailure(), __LINE__);
    2184           5 :   }
    2185     1411565 : }
    2186             : 
    2187             : Real
    2188    46867642 : TraceRay::subdomainHmax(const Elem * elem) const
    2189             : {
    2190             :   const auto subdomain_id = elem->subdomain_id();
    2191    46867642 :   return subdomain_id == _current_subdomain_id ? _current_subdomain_hmax
    2192        2151 :                                                : _study.subdomainHmax(subdomain_id);
    2193             : }
    2194             : 
    2195             : void
    2196    35383257 : TraceRay::postRayTracingObject(const std::shared_ptr<Ray> & ray, const RayTracingObject * rto)
    2197             : {
    2198    35383257 :   if (!ray->shouldContinue())
    2199             :   {
    2200     4075714 :     if (_should_continue)
    2201     1407280 :       _should_continue = false;
    2202             :   }
    2203    31307543 :   else if (!_should_continue)
    2204           0 :     failTrace(rto->typeAndName() +
    2205           0 :                   " set a Ray to continue that was previously set to not continue.\n\n" +
    2206             :                   "Once a Ray has been set to not continue, its continue status cannot change.",
    2207             :               /* warning = */ false,
    2208             :               __LINE__);
    2209             : 
    2210    35383257 :   if (!_should_continue && ray->trajectoryChanged())
    2211           0 :     failTrace(rto->typeAndName() +
    2212           0 :                   " changed the trajectory of a Ray that was set to not continue,\n" +
    2213             :                   "or set a Ray whose trajectory was changed to not continue.",
    2214             :               /* warning = */ false,
    2215             :               __LINE__);
    2216    35383257 : }

Generated by: LCOV version 1.14