LCOV - code coverage report
Current view: top level - src/raytracing - TraceRay.C (source / functions) Hit Total Coverage
Test: idaholab/moose ray_tracing: #32971 (54bef8) with base c6cf66 Lines: 654 686 95.3 %
Date: 2026-05-29 20:39: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        2366 : TraceRay::TraceRay(RayTracingStudy & study, const THREAD_ID tid)
      48        2366 :   : _study(study),
      49        2366 :     _mesh(study.getSubProblem().mesh()),
      50        2366 :     _dim(_mesh.dimension()),
      51        2366 :     _boundary_info(_mesh.getMesh().get_boundary_info()),
      52        2366 :     _pid(_study.comm().rank()),
      53        2366 :     _tid(tid),
      54        2366 :     _backface_culling(false),
      55        2366 :     _current_normals(nullptr),
      56        4732 :     _results(ENDED_STATIONARY + 1)
      57             : {
      58        2366 : }
      59             : 
      60             : void
      61        6201 : TraceRay::preExecute()
      62             : {
      63        6201 :   _current_subdomain_id = Elem::invalid_subdomain_id;
      64        6201 :   _current_elem_type = INVALID_ELEM;
      65             : 
      66             :   // Zero out all results
      67       99216 :   for (auto & val : _results)
      68       93015 :     val = 0;
      69             : 
      70        6201 :   _has_ray_kernels = _study.hasRayKernels(_tid);
      71        6201 :   _is_rectangular_domain = _study.isRectangularDomain();
      72        6201 : }
      73             : 
      74             : void
      75         150 : TraceRay::meshChanged()
      76             : {
      77             :   // Invalidate the vertex and edge neighbor caches
      78             :   _vertex_neighbors.clear();
      79             :   _edge_neighbors.clear();
      80         150 : }
      81             : 
      82             : TraceRay::ExitsElemResult
      83    31429648 : 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    31429648 :   switch (elem_type)
     107             :   {
     108     3894210 :     case HEX8:
     109     3894210 :       intersected = exitsElem<Hex8, Hex8>(elem,
     110             :                                           incoming_side,
     111             :                                           intersection_point,
     112             :                                           intersected_side,
     113             :                                           intersected_extrema,
     114             :                                           intersection_distance,
     115             :                                           normals);
     116     3894210 :       break;
     117     5182938 :     case TET4:
     118     5182938 :       intersected = exitsElem<Tet4, Tet4>(elem,
     119             :                                           incoming_side,
     120             :                                           intersection_point,
     121             :                                           intersected_side,
     122             :                                           intersected_extrema,
     123             :                                           intersection_distance,
     124             :                                           normals);
     125     5182938 :       break;
     126     1685591 :     case PYRAMID5:
     127     1685591 :       intersected = exitsElem<Pyramid5, Pyramid5>(elem,
     128             :                                                   incoming_side,
     129             :                                                   intersection_point,
     130             :                                                   intersected_side,
     131             :                                                   intersected_extrema,
     132             :                                                   intersection_distance,
     133             :                                                   normals);
     134     1685591 :       break;
     135     1276453 :     case PRISM6:
     136     1276453 :       intersected = exitsElem<Prism6, Prism6>(elem,
     137             :                                               incoming_side,
     138             :                                               intersection_point,
     139             :                                               intersected_side,
     140             :                                               intersected_extrema,
     141             :                                               intersection_distance,
     142             :                                               normals);
     143     1276453 :       break;
     144      588371 :     case QUAD4:
     145      588371 :       intersected = exitsElem<Quad4, Quad4>(elem,
     146             :                                             incoming_side,
     147             :                                             intersection_point,
     148             :                                             intersected_side,
     149             :                                             intersected_extrema,
     150             :                                             intersection_distance,
     151             :                                             normals);
     152      588371 :       break;
     153       36011 :     case TRI3:
     154       36011 :       intersected = exitsElem<Tri3, Tri3>(elem,
     155             :                                           incoming_side,
     156             :                                           intersection_point,
     157             :                                           intersected_side,
     158             :                                           intersected_extrema,
     159             :                                           intersection_distance,
     160             :                                           normals);
     161       36011 :       break;
     162     1191279 :     case HEX20:
     163     1191279 :       intersected = exitsElem<Hex20, Hex8>(elem,
     164             :                                            incoming_side,
     165             :                                            intersection_point,
     166             :                                            intersected_side,
     167             :                                            intersected_extrema,
     168             :                                            intersection_distance,
     169             :                                            normals);
     170     1191279 :       break;
     171     1191279 :     case HEX27:
     172     1191279 :       intersected = exitsElem<Hex27, Hex8>(elem,
     173             :                                            incoming_side,
     174             :                                            intersection_point,
     175             :                                            intersected_side,
     176             :                                            intersected_extrema,
     177             :                                            intersection_distance,
     178             :                                            normals);
     179     1191279 :       break;
     180       19460 :     case QUAD8:
     181       19460 :       intersected = exitsElem<Quad8, Quad4>(elem,
     182             :                                             incoming_side,
     183             :                                             intersection_point,
     184             :                                             intersected_side,
     185             :                                             intersected_extrema,
     186             :                                             intersection_distance,
     187             :                                             normals);
     188       19460 :       break;
     189       19460 :     case QUAD9:
     190       19460 :       intersected = exitsElem<Quad9, Quad4>(elem,
     191             :                                             incoming_side,
     192             :                                             intersection_point,
     193             :                                             intersected_side,
     194             :                                             intersected_extrema,
     195             :                                             intersection_distance,
     196             :                                             normals);
     197       19460 :       break;
     198       29506 :     case TRI6:
     199       29506 :       intersected = exitsElem<Tri6, Tri3>(elem,
     200             :                                           incoming_side,
     201             :                                           intersection_point,
     202             :                                           intersected_side,
     203             :                                           intersected_extrema,
     204             :                                           intersection_distance,
     205             :                                           normals);
     206       29506 :       break;
     207       29506 :     case TRI7:
     208       29506 :       intersected = exitsElem<Tri7, Tri3>(elem,
     209             :                                           incoming_side,
     210             :                                           intersection_point,
     211             :                                           intersected_side,
     212             :                                           intersected_extrema,
     213             :                                           intersection_distance,
     214             :                                           normals);
     215       29506 :       break;
     216     5177636 :     case TET10:
     217     5177636 :       intersected = exitsElem<Tet10, Tet4>(elem,
     218             :                                            incoming_side,
     219             :                                            intersection_point,
     220             :                                            intersected_side,
     221             :                                            intersected_extrema,
     222             :                                            intersection_distance,
     223             :                                            normals);
     224     5177636 :       break;
     225     5177636 :     case TET14:
     226     5177636 :       intersected = exitsElem<Tet14, Tet4>(elem,
     227             :                                            incoming_side,
     228             :                                            intersection_point,
     229             :                                            intersected_side,
     230             :                                            intersected_extrema,
     231             :                                            intersection_distance,
     232             :                                            normals);
     233     5177636 :       break;
     234     1685516 :     case PYRAMID13:
     235     1685516 :       intersected = exitsElem<Pyramid13, Pyramid5>(elem,
     236             :                                                    incoming_side,
     237             :                                                    intersection_point,
     238             :                                                    intersected_side,
     239             :                                                    intersected_extrema,
     240             :                                                    intersection_distance,
     241             :                                                    normals);
     242     1685516 :       break;
     243     1685516 :     case PYRAMID14:
     244     1685516 :       intersected = exitsElem<Pyramid14, Pyramid5>(elem,
     245             :                                                    incoming_side,
     246             :                                                    intersection_point,
     247             :                                                    intersected_side,
     248             :                                                    intersected_extrema,
     249             :                                                    intersection_distance,
     250             :                                                    normals);
     251     1685516 :       break;
     252     1276453 :     case PRISM15:
     253     1276453 :       intersected = exitsElem<Prism15, Prism6>(elem,
     254             :                                                incoming_side,
     255             :                                                intersection_point,
     256             :                                                intersected_side,
     257             :                                                intersected_extrema,
     258             :                                                intersection_distance,
     259             :                                                normals);
     260     1276453 :       break;
     261     1276453 :     case PRISM18:
     262     1276453 :       intersected = exitsElem<Prism18, Prism6>(elem,
     263             :                                                incoming_side,
     264             :                                                intersection_point,
     265             :                                                intersected_side,
     266             :                                                intersected_extrema,
     267             :                                                intersection_distance,
     268             :                                                normals);
     269     1276453 :       break;
     270        5966 :     case EDGE2:
     271        5966 :       intersected = exitsElem<Edge2, Edge2>(elem,
     272             :                                             incoming_side,
     273             :                                             intersection_point,
     274             :                                             intersected_side,
     275             :                                             intersected_extrema,
     276             :                                             intersection_distance,
     277             :                                             normals);
     278        5966 :       break;
     279         204 :     case EDGE3:
     280         204 :       intersected = exitsElem<Edge3, Edge2>(elem,
     281             :                                             incoming_side,
     282             :                                             intersection_point,
     283             :                                             intersected_side,
     284             :                                             intersected_extrema,
     285             :                                             intersection_distance,
     286             :                                             normals);
     287         204 :       break;
     288         204 :     case EDGE4:
     289         204 :       intersected = exitsElem<Edge4, Edge2>(elem,
     290             :                                             incoming_side,
     291             :                                             intersection_point,
     292             :                                             intersected_side,
     293             :                                             intersected_extrema,
     294             :                                             intersection_distance,
     295             :                                             normals);
     296         204 :       break;
     297           0 :     default:
     298           0 :       mooseError(
     299           0 :           "Element type ", Utility::enum_to_string(elem->type()), " not supported by TraceRay");
     300             :   }
     301             : 
     302    31429648 :   if (intersected)
     303             :   {
     304    24902341 :     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    31423274 : 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    31423274 :   ++_results[INTERSECTION_CALLS];
     323             : 
     324             :   debugRay("Called exitsElem() in 2D or 3D");
     325             : 
     326    31423274 :   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    31423274 :   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    31423274 :   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    31423274 :   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   149031228 :       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   148960087 :         if (s == incoming_side)
     369             :         {
     370             :           debugRay("    Skipping due to incoming side");
     371    29786003 :           if (++s == T::num_sides)
     372             :             break;
     373             :           else
     374    21993910 :             continue;
     375             :         }
     376             : 
     377             :         // Using backface culling on this run through the sides
     378             :         // If the direction is non-entrant, skip this side
     379   119174084 :         if (use_backface_culling)
     380             :         {
     381             :           // Side is non-entrant per the culling, so skip
     382     6110997 :           if (normals[s] * direction < -LOOSE_TRACE_TOLERANCE)
     383             :           {
     384             :             debugRay("    Skipping due to backface culling dot = ", normals[s] * direction);
     385             : 
     386     1972071 :             if (++s == T::num_sides)
     387             :               break;
     388             :             else
     389     1606975 :               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   113063087 :         else if (_backface_culling)
     399             :         {
     400             :           // Side was non-entrant per the culling, so try again
     401     1601280 :           if (normals[s] * direction >= -LOOSE_TRACE_TOLERANCE)
     402             :           {
     403             :             debugRay("    Skipping because we already checked this side with culling enabled");
     404     1045344 :             if (++s == T::num_sides)
     405             :               break;
     406             :             else
     407      855514 :               continue;
     408             :           }
     409             :           else
     410             :             debugRay("    Side that was skipped due to culling");
     411             : 
     412      555936 :           ++_results[BACKFACE_CULLING_FAILURES];
     413             :         }
     414             :       }
     415             : 
     416             :       // Look for an intersection!
     417   116227810 :       current_intersection_point = RayTracingCommon::invalid_point;
     418             :       current_intersected_extrema.invalidate();
     419   116227810 :       const bool intersected = sideIntersectedByLine<FirstOrderT>(elem,
     420   116227810 :                                                                   _incoming_point,
     421             :                                                                   direction,
     422             :                                                                   s,
     423   116227810 :                                                                   _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   116227810 :       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    27316037 :         if (current_intersection_distance > best_intersection_distance)
     456             :         {
     457             :           debugRay("    Best intersection so far");
     458             : 
     459    25352837 :           intersected_side = s;
     460    25352837 :           intersection_distance = current_intersection_distance;
     461    25352837 :           intersection_point = current_intersection_point;
     462             :           intersected_extrema = current_intersected_extrema;
     463             :           best_intersection_distance = current_intersection_distance;
     464             :         }
     465             :       }
     466             : 
     467   116227810 :       if (++s == T::num_sides || try_nonplanar_incoming_side)
     468             :         break;
     469             :       else
     470    92759198 :         continue;
     471             :     } // while(true)
     472             : 
     473             :     // Found an intersection
     474    31815631 :     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     6919652 :     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     6848511 :     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      321216 :       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     6527295 :     if (_incoming_side != RayTracingCommon::invalid_side &&
     502     6524414 :         _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       71141 :       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        6374 : 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        6374 :   ++_results[INTERSECTION_CALLS];
     526             : 
     527             :   debugRay("Called exitsElem() in 1D");
     528             : 
     529             :   // Scale the tolerance based on the element size
     530        6374 :   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        6374 :   if (incoming_side != RayTracingCommon::invalid_side)
     535             :   {
     536        6054 :     intersected_side = (incoming_side == 1 ? 0 : 1);
     537             :     intersected_extrema.setVertex(intersected_side);
     538        6054 :     intersection_point = elem->point(intersected_side);
     539        6054 :     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        6054 :     return true;
     545             :   }
     546             : 
     547             :   // End point that is for sure out of the element
     548             :   const Point extended_end_point =
     549         320 :       _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         526 :   for (MooseIndex(elem->n_sides()) side = 0; side < elem->n_sides(); ++side)
     554             :   {
     555         514 :     const Point side_point = elem->point(side);
     556             :     debugRay("  Checking side ", side, " at ", side_point);
     557             : 
     558         514 :     const Real incoming_to_side = (side_point - _incoming_point).norm();
     559         514 :     if (incoming_to_side < tol)
     560             :     {
     561             :       debugRay("    Continuing because at side");
     562         134 :       continue;
     563             :     }
     564             : 
     565         380 :     const Real incoming_to_end = (extended_end_point - _incoming_point).norm();
     566         380 :     const Real side_to_end = (extended_end_point - side_point).norm();
     567         380 :     const Real sum = incoming_to_side + side_to_end - incoming_to_end;
     568             :     debugRay("    Sum = ", sum);
     569             : 
     570         380 :     if (std::abs(sum) < tol)
     571             :     {
     572         308 :       intersected_side = side;
     573             :       intersected_extrema.setVertex(side);
     574         308 :       intersection_point = side_point;
     575         308 :       intersection_distance = incoming_to_side;
     576             :       debugRay("    Intersected at ", intersection_point);
     577         308 :       return true;
     578             :     }
     579             :   }
     580             : 
     581             :   return false;
     582             : }
     583             : 
     584             : TraceRay::ExitsElemResult
     585     2586642 : 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     2586642 :   ++_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    15453042 :   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    12866400 :     if (neighbor_info._elem == last_elem)
     625             :     {
     626             :       debugRay("Skipping last elem ", last_elem->id());
     627             :       last_elem_info = &neighbor_info;
     628     2588572 :       continue;
     629             :     }
     630             : 
     631    10277828 :     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    10277828 :     if (exit_result != NO_EXIT)
     640             :     {
     641             :       debugRay("Ray can exit through neighbor ", neighbor_info._elem->id());
     642             : 
     643     3770989 :       if (current_intersection_distance > longest_distance)
     644             :       {
     645     2662424 :         best_elem = neighbor_info._elem;
     646     2662424 :         best_elem_incoming_side = current_incoming_side;
     647     2662424 :         _intersection_point = current_intersection_point;
     648     2662424 :         _intersected_side = current_intersected_side;
     649             :         _intersected_extrema = current_intersected_extrema;
     650     2662424 :         _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     2586642 :   if (!best_elem && last_elem_info)
     659             :   {
     660         276 :     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         276 :     if (exit_result != NO_EXIT && current_intersection_distance > longest_distance)
     668             :     {
     669             :       debugRay("Ray can exit through last_elem ", last_elem->id());
     670             : 
     671         276 :       best_elem = last_elem;
     672         276 :       best_elem_incoming_side = current_incoming_side;
     673         276 :       _intersection_point = current_intersection_point;
     674         276 :       _intersected_side = current_intersected_side;
     675             :       _intersected_extrema = current_intersected_extrema;
     676         276 :       _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     2586642 :   return best_exit_result;
     695             : }
     696             : 
     697             : TraceRay::ExitsElemResult
     698    10278104 : 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    10278104 :   if (!neighbor_info._valid)
     706             :     return NO_EXIT;
     707             : 
     708    10277242 :   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    10277242 :   incoming_side = RayTracingCommon::invalid_side;
     713    13539636 :   for (MooseIndex(neighbor_info._sides.size()) i = 0; i < neighbor_info._sides.size(); ++i)
     714    13537343 :     if (neighbor_info._side_normals[i] * (*_current_ray)->direction() < LOOSE_TRACE_TOLERANCE)
     715             :     {
     716    10274949 :       incoming_side = neighbor_info._sides[i];
     717    10274949 :       break;
     718             :     }
     719             : 
     720             :   // No entrant sides on this neighbor were found
     721    10277242 :   if (incoming_side == RayTracingCommon::invalid_side)
     722             :     return NO_EXIT;
     723             : 
     724    10274949 :   intersection_point = RayTracingCommon::invalid_point;
     725    10274949 :   intersected_side = RayTracingCommon::invalid_side;
     726             :   intersected_extrema.invalidate();
     727    10274949 :   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    10274949 :       exitsElem(neighbor,
     733    10274949 :                 neighbor->type(),
     734    10274949 :                 incoming_side,
     735             :                 intersection_point,
     736             :                 intersected_side,
     737             :                 intersected_extrema,
     738             :                 intersection_distance,
     739    10274949 :                 _backface_culling ? _study.getElemNormals(neighbor, _tid) : nullptr);
     740             :   debugRay("Done with exitsElem() from moveThroughNeighbor()");
     741             : 
     742    10274949 :   return exit_result;
     743             : }
     744             : 
     745             : void
     746      972415 : 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      972415 :   _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      972415 :   if (_dim != 1 && _intersected_extrema.atExtrema())
     760             :   {
     761      587234 :     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     2253514 :     for (const auto & neighbor_info : neighbors)
     770             :     {
     771     1666280 :       if (!neighbor_info._valid)
     772           0 :         continue;
     773             : 
     774     1666280 :       const Elem * elem = neighbor_info._elem;
     775             :       const auto & sides = neighbor_info._sides;
     776             :       const auto & side_normals = neighbor_info._side_normals;
     777             : 
     778     5950241 :       for (MooseIndex(side_normals.size()) i = 0; i < side_normals.size(); ++i)
     779     4283961 :         if (!elem->neighbor_ptr(sides[i]) // is a boundary side that has our point
     780     4283961 :             && side_normals[i] * ray->direction() > TRACE_TOLERANCE) // and is entrant
     781             :         {
     782             :           // TODO: this could likely be optimized
     783             :           ElemExtrema extrema;
     784     1420123 :           withinExtremaOnSide(elem, _intersection_point, sides[i], _dim, extrema);
     785             : 
     786     1420123 :           _boundary_info.boundary_ids(elem, sides[i], _boundary_ids);
     787     1420123 :           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      385181 :     _boundary_info.boundary_ids(_current_elem, _intersected_side, _boundary_ids);
     796      385181 :     possiblyAddToBoundaryElems(
     797      385181 :         _current_elem, _intersected_side, _boundary_ids, _intersected_extrema);
     798             :   }
     799             : 
     800             :   debugRay("Calling external onBoundary() with ", _boundary_elems.size(), " boundaries");
     801      972415 :   onBoundary(ray, /* external = */ true);
     802      972405 : }
     803             : 
     804             : void
     805        1729 : 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        1729 :   _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        1729 :   if (_dim != 1 && _intersected_extrema.atExtrema())
     828             :   {
     829             :     debugRay("Checking point neighbors for internal sidesets");
     830             : 
     831             :     // Get the neighbors
     832        1182 :     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       10362 :     for (const auto & neighbor_info : neighbors)
     841             :     {
     842        9180 :       if (!neighbor_info._valid)
     843           0 :         continue;
     844             : 
     845        9180 :       const Elem * elem = neighbor_info._elem;
     846             : 
     847             :       // Grab the internal sidesets for this elem
     848        9180 :       const auto & sidesets = _study.getInternalSidesets(elem);
     849             :       // It has none to contribute, so we can continue
     850        9180 :       if (sidesets.empty())
     851             :       {
     852             :         debugRay("    Elem ", elem->id(), " has no internal sidesets");
     853        7500 :         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        6036 :       for (std::size_t i = 0; i < sides.size(); ++i)
     862             :       {
     863        4356 :         const auto side = sides[i];
     864             :         // Side has internal sidesets and is entrant
     865        4356 :         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        1164 :           withinExtremaOnSide(elem, _intersection_point, side, _dim, temp_extrema);
     870             : 
     871        1164 :           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         547 :     const auto & current_elem_sidesets = _study.getInternalSidesets(_current_elem);
     881         547 :     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         197 :       if (!_study.hasSameLevelActiveElems())
     889           0 :         mooseError("Internal sidesets are not currently supported with adaptivity in tracing");
     890             : 
     891             :       // Special case for 1D
     892         197 :       if (_dim == 1)
     893          26 :         temp_extrema.setVertex(atVertex(_current_elem, _intersection_point));
     894             : 
     895         197 :       possiblyAddToBoundaryElems(_current_elem,
     896             :                                  _incoming_side,
     897         197 :                                  current_elem_sidesets[_incoming_side],
     898         197 :                                  _dim != 1 ? _intersected_extrema : temp_extrema);
     899             :     }
     900             : 
     901         547 :     const auto & last_elem_sidesets = _study.getInternalSidesets(_last_elem);
     902         547 :     if (last_elem_sidesets.size() && last_elem_sidesets[_intersected_side].size())
     903         218 :       possiblyAddToBoundaryElems(_last_elem,
     904             :                                  _intersected_side,
     905             :                                  last_elem_sidesets[_intersected_side],
     906         218 :                                  _intersected_extrema);
     907             :   }
     908             : 
     909        1729 :   if (!_boundary_elems.empty())
     910             :   {
     911             :     debugRay("  Calling internal onBoundary() with ", _boundary_elems.size(), " boundaries");
     912         856 :     onBoundary(ray, /* external = */ false);
     913             :   }
     914        1725 : }
     915             : 
     916             : void
     917     1806883 : 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     3613766 :   for (const auto bnd_id : bnd_ids)
     926             :   {
     927             :     bool found = false;
     928     1920959 :     for (const auto & bnd_elem : _boundary_elems)
     929      878679 :       if (bnd_elem.bnd_id == bnd_id)
     930             :       {
     931             :         found = true;
     932             :         break;
     933             :       }
     934             : 
     935     1806883 :     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     1042280 :       _boundary_elems.emplace_back(elem, side, bnd_id, extrema);
     945             :     }
     946             :   }
     947     1806883 : }
     948             : 
     949             : void
     950      767413 : 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      767413 :   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     4365852 :   for (unsigned short s = 0; s < _current_elem_n_sides; ++s)
     971     4664589 :     if (!_current_elem->neighbor_ptr(s) && s != _intersected_side &&
     972     1722049 :         _current_elem->is_node_on_side(_intersected_extrema.first, s) &&
     973     5045174 :         (!at_edge || _current_elem->is_node_on_side(_intersected_extrema.second, s)) &&
     974      732631 :         !_study.sideIsIncoming(_current_elem, s, direction, _tid))
     975             :     {
     976             :       debugRay("  Side ", s, " is a boundary side and the Ray exits");
     977      125034 :       boundary_side = s;
     978             :       boundary_extrema = _intersected_extrema;
     979      125034 :       boundary_elem = _current_elem;
     980      125034 :       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      642379 :   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     2229898 :   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     1696705 :     if (!neighbor_info._valid)
     994           0 :       continue;
     995             : 
     996     1696705 :     const Elem * neighbor = neighbor_info._elem;
     997             :     // We've already checked ourself
     998     1696705 :     if (neighbor == _current_elem)
     999      579305 :       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     3495090 :     for (MooseIndex(neighbor_info._sides.size()) i = 0; i < neighbor_info._sides.size(); ++i)
    1005     2486876 :       if (neighbor_info._side_normals[i] * direction > TRACE_TOLERANCE &&
    1006      542017 :           !neighbor->neighbor_ptr(neighbor_info._sides[i]))
    1007             :       {
    1008      109186 :         withinExtremaOnSide(
    1009      109186 :             neighbor, _intersection_point, neighbor_info._sides[i], _dim, boundary_extrema);
    1010             :         traceAssert(boundary_extrema.atExtrema(), "Should be at extrema");
    1011      109186 :         boundary_side = neighbor_info._sides[i];
    1012      109186 :         boundary_elem = neighbor;
    1013             :         return;
    1014             :       }
    1015             :   }
    1016             : }
    1017             : 
    1018             : void
    1019     3267644 : TraceRay::trace(const std::shared_ptr<Ray> & ray)
    1020             : {
    1021             :   mooseAssert(_study.currentlyPropagating(), "Should only use while propagating rays");
    1022             : 
    1023     3267644 :   _current_ray = &ray;
    1024     3267644 :   _current_elem = ray->currentElem();
    1025     3267644 :   _last_elem = nullptr;
    1026     3267644 :   _incoming_point = ray->currentPoint();
    1027     3267644 :   _incoming_side = ray->currentIncomingSide();
    1028     3267644 :   _should_continue = true;
    1029             : 
    1030     3267644 :   _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     3267644 :   if (_study.verifyRays() && !ray->invalidCurrentIncomingSide() && ray->maxDistance() > 0 &&
    1037     5861901 :       !_study.sideIsNonPlanar(_current_elem, _incoming_side) &&
    1038     1286038 :       !_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 immediately 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     3267644 :   if (_study.shouldCacheTrace(ray))
    1064             :   {
    1065             :     debugRay("Trying to init threaded cached trace");
    1066             : 
    1067        8576 :     _current_cached_trace = &_study.initThreadedCachedTrace(ray, _tid);
    1068             : 
    1069             :     // Add starting data
    1070        8576 :     if (_study.dataOnCacheTraces())
    1071         904 :       _current_cached_trace->lastPoint()._data = ray->data();
    1072        8576 :     if (_study.auxDataOnCacheTraces())
    1073         876 :       _current_cached_trace->lastPoint()._aux_data = ray->auxData();
    1074             :   }
    1075             :   else
    1076     3259068 :     _current_cached_trace = nullptr;
    1077             : 
    1078             :   // Need to call subdomain setup
    1079     3267644 :   if (_current_elem->subdomain_id() != _current_subdomain_id || _study.rayDependentSubdomainSetup())
    1080     3267644 :     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    23718768 :     _exits_elem = false;
    1110    23718768 :     _intersection_point = RayTracingCommon::invalid_point;
    1111    23718768 :     _intersected_side = RayTracingCommon::invalid_side;
    1112             :     _intersected_extrema.invalidate();
    1113    23718768 :     _intersection_distance = RayTracingCommon::invalid_distance;
    1114             : 
    1115             :     // Stationary ray
    1116    23718768 :     if (ray->stationary())
    1117             :     {
    1118             :       mooseAssert(ray->invalidDirection(), "Should have an invalid direction");
    1119        1050 :       _exits_elem = true;
    1120        1050 :       _intersection_point = _incoming_point;
    1121             :       _intersected_extrema = _last_intersected_extrema;
    1122        1050 :       _intersection_distance = 0;
    1123        1050 :       ++_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    23717718 :     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    21154699 :       if (_backface_culling)
    1137      630809 :         _current_normals = _study.getElemNormals(_current_elem, _tid);
    1138             : 
    1139    21154699 :       const auto exits_elem_result = exitsElem(_current_elem,
    1140             :                                                _current_elem_type,
    1141    21154699 :                                                _incoming_side,
    1142    21154699 :                                                _intersection_point,
    1143    21154699 :                                                _intersected_side,
    1144    21154699 :                                                _intersected_extrema,
    1145    21154699 :                                                _intersection_distance,
    1146             :                                                _current_normals);
    1147             : 
    1148    21154699 :       if (exits_elem_result != NO_EXIT)
    1149             :       {
    1150    21131076 :         storeExitsElemResult(exits_elem_result);
    1151    21131076 :         _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    23718768 :     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     2586642 :       if (_last_intersected_extrema.atExtrema())
    1178             :       {
    1179             :         traceAssert(_last_elem, "Should be valid");
    1180     2563019 :         move_through_neighbors_last = _last_elem;
    1181     2563019 :         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       23623 :         move_through_neighbors_last = _current_elem;
    1197             : 
    1198             :         // If we have side info: check for vertices on said side, otherwise, check everywhere
    1199       23623 :         const auto at_v = _incoming_side != RayTracingCommon::invalid_side
    1200       23623 :                               ? atVertexOnSide(_current_elem, _incoming_point, _incoming_side)
    1201         971 :                               : atVertex(_current_elem, _incoming_point);
    1202             : 
    1203       23623 :         if (at_v != RayTracingCommon::invalid_vertex)
    1204       23601 :           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          22 :         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       23601 :         if (!neighbors || neighbors->empty())
    1221          22 :           neighbors = &getPointNeighbors(_current_elem, _incoming_point);
    1222             : 
    1223             :         // Couldn't find anything
    1224       23623 :         if (neighbors->empty())
    1225             :         {
    1226           0 :           failTrace("Could not find neighbors to move through", _study.tolerateFailure(), __LINE__);
    1227      101273 :           return;
    1228             :         }
    1229             :       }
    1230             : 
    1231             :       // Move through a neighbor
    1232     2586642 :       const Elem * best_neighbor = nullptr;
    1233     2586642 :       auto best_neighbor_side = RayTracingCommon::invalid_side;
    1234     2586642 :       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     2586642 :       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     2586642 :       _exits_elem = true;
    1249     2586642 :       _last_elem = _current_elem;
    1250     2586642 :       _current_elem = best_neighbor;
    1251     2586642 :       _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     2586642 :       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      101273 :         _intersection_distance = RayTracingCommon::invalid_distance;
    1264             : 
    1265      101273 :         continueTraceOffProcessor(ray);
    1266             :         return;
    1267             :       }
    1268             : 
    1269             :       // Subdomain changed
    1270     2485369 :       if (_current_elem->subdomain_id() != _current_subdomain_id)
    1271         146 :         onSubdomainChanged(ray, /* same_ray = */ true);
    1272             : 
    1273             :       // Do own this element - tally the result as we're the ones tracing it
    1274     2485369 :       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    23617495 :     if (_intersection_distance > 0)
    1290             :     {
    1291             :       debugRay("Incrementing ray intersections by 1 to ", ray->intersections() + 1);
    1292             :       ray->addIntersection();
    1293    23616445 :       _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    23700535 :     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    23617495 :     if (MooseUtils::absoluteFuzzyEqual(ray->distance(), max_distance))
    1311             :     {
    1312             :       debugRay("At max distance");
    1313             : 
    1314             :       ray->setShouldContinue(false);
    1315       15042 :       _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    23602453 :     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     1801528 :       _intersection_point -= ray->direction() * difference;
    1332     1801528 :       _intersection_distance -= difference;
    1333     1801528 :       _intersected_side = RayTracingCommon::invalid_side;
    1334             :       _intersected_extrema.invalidate();
    1335             :       ray->setCurrentPoint(_intersection_point);
    1336             : 
    1337             :       ray->setShouldContinue(false);
    1338     1801528 :       _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    23617495 :     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    22560690 :       onSegment(ray);
    1360             : 
    1361    22560678 :       _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    22560678 :       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     1696000 :         onCompleteTrace(ray);
    1372     1696000 :         return;
    1373             :       }
    1374             : 
    1375             :       // RayKernel moved a Ray
    1376    20864678 :       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         432 :         _incoming_point = ray->currentPoint();
    1385         432 :         _incoming_side = RayTracingCommon::invalid_side;
    1386             :         _intersected_extrema.invalidate();
    1387             :         ray->setCurrentIncomingSide(_incoming_side);
    1388             : 
    1389         432 :         const auto new_intersection_distance = (ray->currentPoint() - _incoming_point).norm();
    1390         432 :         ray->addDistance(-_intersection_distance + new_intersection_distance);
    1391         432 :         _intersection_distance = new_intersection_distance;
    1392             : 
    1393         432 :         onTrajectoryChanged(ray);
    1394         432 :         onContinueTrace(ray);
    1395             :         continue;
    1396         432 :       }
    1397             :       else
    1398             :         possiblyAddDebugRayMeshPoint(_incoming_point, _intersection_point);
    1399             :     }
    1400             :     else
    1401             :     {
    1402     1056805 :       _study.postOnSegment(_tid, ray);
    1403             : 
    1404     1056805 :       if (!_should_continue)
    1405             :       {
    1406             :         debugRay("Killing due to at end without RayKernels");
    1407             :         traceAssert(!ray->shouldContinue(), "Ray shouldn't continue");
    1408             : 
    1409      120568 :         onCompleteTrace(ray);
    1410      120568 :         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    21794633 :     if (_dim > 1 && _intersected_extrema.atExtrema() &&
    1420    24944807 :         _current_elem->neighbor_ptr(_intersected_side) &&
    1421     5582620 :         (!_is_rectangular_domain ||
    1422     2791310 :          onBoundingBoxBoundary(_study.boundingBox(),
    1423     2791310 :                                _intersection_point,
    1424             :                                _dim,
    1425     2791310 :                                LOOSE_TRACE_TOLERANCE * _study.domainMaxLength())))
    1426             :     {
    1427      767413 :       auto boundary_side = RayTracingCommon::invalid_side;
    1428             :       ElemExtrema boundary_extrema;
    1429      767413 :       const Elem * boundary_elem = nullptr;
    1430             : 
    1431      767413 :       findExternalBoundarySide(boundary_side, boundary_extrema, boundary_elem);
    1432             : 
    1433      767413 :       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      234220 :         _last_elem = _current_elem;
    1440      234220 :         _current_elem = boundary_elem;
    1441      234220 :         _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    21800483 :     _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    21800483 :     neighbor = _study.hasSameLevelActiveElems()
    1466    21800483 :                    ? _current_elem->neighbor_ptr(_intersected_side)
    1467      126255 :                    : getActiveNeighbor(_current_elem, _intersected_side, _intersection_point);
    1468             : 
    1469             :     // Found one - not on the boundary so set the next info
    1470    21800483 :     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    20828068 :       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    20711077 :         const unsigned short n_sides = neighbor->subdomain_id() == _current_subdomain_id
    1484             :                                            ? _current_elem_n_sides
    1485    20711077 :                                            : neighbor->n_sides();
    1486             :         traceAssert(n_sides == neighbor->n_sides(), "n_sides incorrect");
    1487             : 
    1488    63285502 :         for (_incoming_side = 0; _incoming_side < n_sides; ++_incoming_side)
    1489    63285502 :           if (neighbor->neighbor_ptr(_incoming_side) == _current_elem)
    1490             :             break;
    1491             :       }
    1492             :       else
    1493      116991 :         _incoming_side = neighbor->which_neighbor_am_i(_current_elem);
    1494             : 
    1495    20828068 :       _last_elem = _current_elem;
    1496    20828068 :       _current_elem = neighbor;
    1497             :       ray->setCurrentElem(neighbor);
    1498    20828068 :       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    20828068 :       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    20828068 :       if (_study.hasInternalSidesets() && (subdomain_changed || _intersected_extrema.atExtrema()))
    1516             :       {
    1517        1729 :         applyOnInternalBoundary(ray);
    1518             : 
    1519             :         // Internal RayBC killed a Ray
    1520        1725 :         if (!_should_continue)
    1521             :         {
    1522             :           traceAssert(!ray->shouldContinue(), "Should be the same");
    1523             :           debugRay("Internal RayBC killed the ray");
    1524             : 
    1525         159 :           onCompleteTrace(ray);
    1526         159 :           return;
    1527             :         }
    1528             : 
    1529             :         // Internal RayBC changed the Ray
    1530        1566 :         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         297 :           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         297 :           if (dot > -TRACE_TOLERANCE)
    1540             :           {
    1541         273 :             _incoming_side = _last_elem->which_neighbor_am_i(_current_elem);
    1542         273 :             _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         297 :           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    20827905 :       if (neighbor->processor_id() != _pid)
    1563             :       {
    1564      466730 :         if (_intersected_extrema.atExtrema())
    1565             :         {
    1566             :           debugRay("Neighbor is off processor but continuing to move through neighbors");
    1567             :         }
    1568             :         else
    1569             :         {
    1570      408258 :           continueTraceOffProcessor(ray);
    1571      408258 :           return;
    1572             :         }
    1573             :       }
    1574             : 
    1575             :       // Neighbor is on processor, call subdomain setup if needed
    1576    20419647 :       if (subdomain_changed)
    1577        1687 :         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      972415 :       applyOnExternalBoundary(ray);
    1586             : 
    1587      972405 :       if (_current_elem == ray->currentElem())
    1588             :         traceAssert(ray->currentPoint().absolute_fuzzy_equals(_intersection_point, TRACE_TOLERANCE),
    1589             :                     "RayBC changed the Ray point");
    1590             : 
    1591             :       // Quit tracing if the Ray was killed by a BC
    1592      972405 :       if (!_should_continue)
    1593             :       {
    1594             :         traceAssert(!ray->shouldContinue(), "Should be the same");
    1595             :         debugRay("Exiting due to death by BC");
    1596             : 
    1597      938538 :         onCompleteTrace(ray);
    1598      938538 :         return;
    1599             :       }
    1600             :       // RayBC changed the direction of the Ray
    1601       33867 :       if (ray->trajectoryChanged())
    1602             :       {
    1603             :         possiblyAddDebugRayMeshPoint(_incoming_point, _intersection_point);
    1604             : 
    1605       33867 :         _last_elem = _current_elem;
    1606             :         // Direction changed
    1607       33867 :         if (_current_elem == ray->currentElem())
    1608             :         {
    1609             :           debugRay("RayBC reflected the ray");
    1610             :           debugRay("  new direction = ", ray->direction());
    1611             :           traceAssert(ray->direction() *
    1612             :                               _study.getSideNormal(_current_elem, _intersected_side, _tid) <
    1613             :                           TRACE_TOLERANCE,
    1614             :                       "Reflected ray is not incoming");
    1615             : 
    1616        2181 :           _incoming_point = _intersection_point;
    1617        2181 :           _incoming_side = _intersected_side;
    1618             :           ray->setCurrentPoint(_incoming_point);
    1619             :           ray->setCurrentIncomingSide(_incoming_side);
    1620             :         }
    1621             :         // Position changed (PeriodicRayBC)
    1622             :         else
    1623             :         {
    1624             :           debugRay("RayBC moved the ray");
    1625             :           debugRay("  new point = ", ray->currentPoint());
    1626             :           debugRay("  new pid = ", ray->currentElem()->processor_id());
    1627             :           debugRay("  new elem id = ", ray->currentElem()->id());
    1628             :           debugRay("  new side = ", ray->currentIncomingSide());
    1629             : 
    1630       31686 :           _current_elem = ray->currentElem();
    1631       31686 :           _incoming_point = ray->currentPoint();
    1632       31686 :           _incoming_side = ray->currentIncomingSide();
    1633             :           _intersected_extrema.invalidate();
    1634             : 
    1635       31686 :           if (_current_elem->processor_id() != _pid)
    1636             :           {
    1637        2822 :             continueTraceOffProcessor(ray);
    1638        2822 :             return;
    1639             :           }
    1640             :         }
    1641       31045 :         onTrajectoryChanged(ray);
    1642             :       }
    1643             :     }
    1644             : 
    1645    20450692 :     onContinueTrace(ray);
    1646             : 
    1647             :   } while (true);
    1648             : 
    1649             :   // If a trace made its way down here and didn't return... it failed
    1650             :   failTrace("Could not find an intersection", _study.tolerateFailure(), __LINE__);
    1651             : }
    1652             : 
    1653             : void
    1654     2755265 : TraceRay::onCompleteTrace(const std::shared_ptr<Ray> & ray)
    1655             : {
    1656     5287231 :   for (RayKernelBase * rk : _study.currentRayKernels(_tid))
    1657     2531966 :     rk->postTrace();
    1658             : 
    1659             :   debugRay("Called onCompleteTrace()\n", (*_current_ray)->getInfo());
    1660             :   if (_intersection_distance > 0)
    1661             :     possiblyAddDebugRayMeshPoint(_incoming_point, _intersection_point);
    1662             :   possiblySaveDebugRayMesh();
    1663             : 
    1664     2755265 :   if (_current_cached_trace)
    1665             :   {
    1666        7571 :     _current_cached_trace->_last = true;
    1667             : 
    1668        7571 :     if (_intersection_distance > 0)
    1669             :     {
    1670             :       _current_cached_trace->addPoint(ray->currentPoint());
    1671        6833 :       if (_study.dataOnCacheTraces())
    1672         118 :         _current_cached_trace->lastPoint()._data = ray->data();
    1673        6833 :       if (_study.auxDataOnCacheTraces())
    1674          94 :         _current_cached_trace->lastPoint()._aux_data = ray->auxData();
    1675             :     }
    1676             : 
    1677             :     mooseAssert(ray->stationary() == _current_cached_trace->stationary(), "Stationary mismatch");
    1678             :   }
    1679     2755265 : }
    1680             : 
    1681             : void
    1682    20451124 : TraceRay::onContinueTrace(const std::shared_ptr<Ray> & ray)
    1683             : {
    1684             :   traceAssert(ray->shouldContinue(), "Ray must continue");
    1685             : 
    1686    20451124 :   if (_current_cached_trace && _study.segmentsOnCacheTraces() && _intersection_distance > 0)
    1687             :   {
    1688             :     _current_cached_trace->addPoint(ray->currentPoint());
    1689       30621 :     if (_study.dataOnCacheTraces())
    1690         453 :       _current_cached_trace->lastPoint()._data = ray->data();
    1691       30621 :     if (_study.auxDataOnCacheTraces())
    1692         357 :       _current_cached_trace->lastPoint()._aux_data = ray->auxData();
    1693             :   }
    1694    20451124 : }
    1695             : 
    1696             : void
    1697      512353 : TraceRay::continueTraceOffProcessor(const std::shared_ptr<Ray> & ray)
    1698             : {
    1699             :   traceAssert(ray->currentElem() == _current_elem, "Ray currentElem() invalid");
    1700             :   traceAssert(ray->currentIncomingSide() == _incoming_side, "Ray currentIncomingSide() invalid");
    1701             :   traceAssert(ray->currentPoint() == _incoming_point, "Ray currentPoint() invalid");
    1702             :   traceAssert(_current_elem->processor_id() != _pid, "Off processor trace is not off processor");
    1703             :   debugRay("Ray going off processor to ", _current_elem->processor_id());
    1704             : 
    1705             :   ray->addProcessorCrossing();
    1706             : 
    1707      512353 :   if (_current_cached_trace && _intersection_distance > 0)
    1708             :   {
    1709         894 :     _current_cached_trace->addPoint(_incoming_point);
    1710         894 :     if (_study.dataOnCacheTraces())
    1711          11 :       _current_cached_trace->lastPoint()._data = ray->data();
    1712         894 :     if (_study.auxDataOnCacheTraces())
    1713          11 :       _current_cached_trace->lastPoint()._aux_data = ray->auxData();
    1714             :   }
    1715             : 
    1716             :   if (_intersection_distance > 0)
    1717             :     possiblyAddDebugRayMeshPoint(_incoming_point, _intersection_point);
    1718             :   possiblySaveDebugRayMesh();
    1719      512353 : }
    1720             : 
    1721             : void
    1722       31774 : TraceRay::onTrajectoryChanged(const std::shared_ptr<Ray> & ray)
    1723             : {
    1724             : #ifndef NDEBUG
    1725             :   if (_study.verifyTraceIntersections() &&
    1726             :       (_intersected_extrema.atExtrema()
    1727             :            ? !_current_elem->close_to_point(ray->currentPoint(), LOOSE_TRACE_TOLERANCE)
    1728             :            : !_current_elem->contains_point(ray->currentPoint())))
    1729             :     failTrace("Elem does not contain point after trajectory change",
    1730             :               /* warning = */ false,
    1731             :               __LINE__);
    1732             : #endif
    1733             : 
    1734             :   traceAssert(ray->shouldContinue(), "Ray should continue when trajectory is being changed");
    1735             : 
    1736             :   ray->setTrajectoryChanged(false);
    1737             :   ray->addTrajectoryChange();
    1738             : 
    1739       31774 :   if (_current_cached_trace && !_study.segmentsOnCacheTraces())
    1740             :   {
    1741          40 :     if (_intersection_distance > 0)
    1742             :       _current_cached_trace->addPoint(ray->currentPoint());
    1743          40 :     if (_study.dataOnCacheTraces())
    1744           0 :       _current_cached_trace->lastPoint()._data = ray->data();
    1745          40 :     if (_study.auxDataOnCacheTraces())
    1746           0 :       _current_cached_trace->lastPoint()._aux_data = ray->auxData();
    1747             :   }
    1748       31774 : }
    1749             : 
    1750             : void
    1751     3269477 : TraceRay::onSubdomainChanged(const std::shared_ptr<Ray> & ray, const bool same_ray)
    1752             : {
    1753             :   debugRay("Calling onSubdomainChanged() on subdomain ", _current_elem->subdomain_id());
    1754             :   debugRay("  _current_subdomain_id = ", _current_subdomain_id);
    1755             : 
    1756     3269477 :   _current_subdomain_id = _current_elem->subdomain_id();
    1757     3269477 :   _current_subdomain_hmax = _study.subdomainHmax(_current_subdomain_id);
    1758     3269477 :   _current_elem_type = _current_elem->type();
    1759     3269477 :   _current_elem_n_sides = _current_elem->n_sides();
    1760             : 
    1761     3269477 :   if (_has_ray_kernels)
    1762             :   {
    1763     2997087 :     auto & current_ray_kernels = _study.currentRayKernels(_tid);
    1764             : 
    1765             :     // If we're still tracing the same Ray, keep track of our old RayKernels
    1766             :     // so that we don't call preTrace() on them again
    1767     2997087 :     if (same_ray)
    1768        1130 :       _old_ray_kernels.insert(current_ray_kernels.begin(), current_ray_kernels.end());
    1769             :     // If we're not tracing the same Ray, we need to call preTrace() on everything
    1770             :     else
    1771             :       _old_ray_kernels.clear();
    1772             : 
    1773             :     // Call segmentSubdomainSetup to get new kernels etc
    1774     2997087 :     _study.segmentSubdomainSetup(_current_subdomain_id, _tid, ray->id());
    1775             : 
    1776             :     // Call preTrace() on all of the RayKernels that need it
    1777     5998762 :     for (RayKernelBase * rk : current_ray_kernels)
    1778             :       // Haven't called preTrace() for this Ray on this RayKernel yet
    1779             :       if (!_old_ray_kernels.count(rk))
    1780     3001629 :         rk->preTrace();
    1781             :   }
    1782     3269477 : }
    1783             : 
    1784             : std::string
    1785           8 : TraceRay::failTraceMessage(const std::string & reason, const int line)
    1786             : {
    1787           8 :   std::stringstream oss;
    1788           8 :   oss << "Ray on processor " << _pid << " and thread " << _tid << " failed to trace";
    1789           8 :   if (line != -1)
    1790           8 :     oss << " at line " << line;
    1791          16 :   oss << "\n\n" << reason << "\n\n";
    1792          16 :   oss << ((*_current_ray))->getInfo() << "\n";
    1793           8 :   oss << "Current trace information\n";
    1794           8 :   oss << "  _current_subdomain_id = ";
    1795           8 :   if (_current_subdomain_id == Elem::invalid_subdomain_id)
    1796           0 :     oss << "invalid subdomain id\n";
    1797             :   else
    1798           8 :     oss << _current_subdomain_id << "\n";
    1799          16 :   oss << "  _current_elem_type = " << Utility::enum_to_string(_current_elem_type) << "\n";
    1800           8 :   oss << "  _current_elem_n_sides = " << _current_elem_n_sides << "\n";
    1801           8 :   oss << "  _incoming_point = ";
    1802             :   if (_incoming_point == RayTracingCommon::invalid_point)
    1803           0 :     oss << "invalid point\n";
    1804             :   else
    1805           8 :     oss << _incoming_point << "\n";
    1806           8 :   oss << "  _incoming_side = ";
    1807           8 :   if (_incoming_side == RayTracingCommon::invalid_side)
    1808           0 :     oss << "invalid side\n";
    1809             :   else
    1810           8 :     oss << _incoming_side << "\n";
    1811           8 :   oss << "  _intersection_point = ";
    1812             :   if (_intersection_point == RayTracingCommon::invalid_point)
    1813           0 :     oss << "invalid point\n";
    1814             :   else
    1815           8 :     oss << _intersection_point << "\n";
    1816           8 :   oss << "  _intersected_side = ";
    1817           8 :   if (_intersected_side == RayTracingCommon::invalid_side)
    1818           0 :     oss << "invalid side\n";
    1819             :   else
    1820           8 :     oss << _intersected_side << "\n";
    1821           8 :   oss << "  _intersected_extrema = " << _intersected_extrema << "\n";
    1822           8 :   oss << "  _exits_elem = " << _exits_elem << "\n";
    1823           8 :   if (_current_elem)
    1824          16 :     oss << _current_elem->get_info();
    1825             :   else
    1826           0 :     oss << "_current_elem = invalid\n";
    1827             : 
    1828             :   possiblySaveDebugRayMesh();
    1829           8 :   return oss.str();
    1830           8 : }
    1831             : 
    1832             : void
    1833           8 : TraceRay::failTrace(const std::string & reason, const bool warning, const int line)
    1834             : {
    1835           8 :   const auto message = failTraceMessage(reason, line);
    1836             : 
    1837           8 :   if (warning)
    1838             :   {
    1839           4 :     ++_results[FAILED_TRACES];
    1840             :     mooseWarning(message);
    1841           4 :     (*_current_ray)->setShouldContinue(false);
    1842           4 :     _should_continue = false;
    1843             :   }
    1844             :   else
    1845           4 :     mooseError(message);
    1846           4 : }
    1847             : 
    1848             : const std::vector<NeighborInfo> &
    1849      933020 : TraceRay::getVertexNeighbors(const Elem * elem, const Node * vertex)
    1850             : {
    1851             :   traceAssert(elem, "Elem must be valid");
    1852             :   traceAssert(vertex, "Vertex must be valid");
    1853             : 
    1854             :   debugRay("Called getVertexNeighbors() with:");
    1855             :   debugRay("  elem->id() = ", elem->id(), " with centroid ", elem->vertex_average());
    1856             :   debugRay("  vertex->id() = ", vertex->id(), ", at ", (Point)*vertex);
    1857             : 
    1858             :   traceAssert(elem->get_node_index(vertex) != libMesh::invalid_uint, "Doesn't contain node");
    1859             :   traceAssert(elem->is_vertex(elem->get_node_index(vertex)), "Node is not a vertex");
    1860             : 
    1861      933020 :   ++_results[VERTEX_NEIGHBOR_LOOKUPS];
    1862             : 
    1863             :   // Return the entry if we have it cached
    1864             :   auto search = _vertex_neighbors.find(vertex);
    1865      933020 :   if (search != _vertex_neighbors.end())
    1866      866504 :     return search->second;
    1867             : 
    1868       66516 :   ++_results[VERTEX_NEIGHBOR_BUILDS];
    1869             : 
    1870             :   // Make a new entry
    1871             :   debugRay("Building vertex neighbors");
    1872             :   std::vector<NeighborInfo> & entry =
    1873       66516 :       _vertex_neighbors.emplace(vertex, std::vector<NeighborInfo>()).first->second;
    1874             : 
    1875       66516 :   findNodeNeighbors(elem,
    1876             :                     vertex,
    1877       66516 :                     _neighbor_set,
    1878       66516 :                     _neighbor_untested_set,
    1879       66516 :                     _neighbor_next_untested_set,
    1880       66516 :                     _neighbor_active_neighbor_children,
    1881             :                     entry);
    1882             : 
    1883             :   // Fill the side normals
    1884      505463 :   for (auto & neighbor_info : entry)
    1885     1759784 :     for (MooseIndex(neighbor_info._sides.size()) i = 0; i < neighbor_info._sides.size(); ++i)
    1886     1320837 :       neighbor_info._side_normals[i] =
    1887     1320837 :           _study.getSideNormal(neighbor_info._elem, neighbor_info._sides[i], _tid);
    1888             : 
    1889             :   return entry;
    1890             : }
    1891             : 
    1892             : const std::vector<NeighborInfo> &
    1893      933020 : TraceRay::getVertexNeighbors(const Elem * elem, const unsigned short vertex)
    1894             : {
    1895             :   traceAssert(vertex < elem->n_vertices(), "Invalid vertex");
    1896             : 
    1897      933020 :   return getVertexNeighbors(elem, elem->node_ptr(vertex));
    1898             : }
    1899             : 
    1900             : const std::vector<NeighborInfo> &
    1901     2884395 : TraceRay::getEdgeNeighbors(const Elem * elem,
    1902             :                            const std::pair<const Node *, const Node *> & vertices,
    1903             :                            const Point & point)
    1904             : {
    1905             :   traceAssert(elem, "Invalid elem");
    1906             :   traceAssert(vertices.first, "Must be valid");
    1907             :   traceAssert(vertices.second, "Must be valid");
    1908             : 
    1909             :   debugRay("Called getEdgeNeighbors() with:");
    1910             :   debugRay("  elem->id() = ", elem->id(), " with centroid ", elem->vertex_average());
    1911             :   debugRay("  vertices.first = ", vertices.first->id(), " at ", (Point)*vertices.first);
    1912             :   debugRay("  vertices.second = ", vertices.second->id(), " at ", (Point)*vertices.second);
    1913             :   debugRay("  point = ", point);
    1914             : 
    1915             :   traceAssert(elem->get_node_index(vertices.first) != libMesh::invalid_uint,
    1916             :               "Doesn't contain vertex");
    1917             :   traceAssert(elem->get_node_index(vertices.second) != libMesh::invalid_uint,
    1918             :               "Doesn't contain vertex");
    1919             :   traceAssert(isWithinSegment(
    1920             :                   (Point)*vertices.first, (Point)*vertices.second, point, LOOSE_TRACE_TOLERANCE),
    1921             :               "Point not within edge");
    1922             : 
    1923     2884395 :   ++_results[EDGE_NEIGHBOR_LOOKUPS];
    1924             : 
    1925     2884395 :   const auto ordered_vertices = vertices.first->id() < vertices.second->id()
    1926     2884395 :                                     ? vertices
    1927     2884395 :                                     : std::make_pair(vertices.second, vertices.first);
    1928             : 
    1929             :   // Look for the entry and build if necessary
    1930             :   std::pair<bool, std::vector<NeighborInfo>> * entry;
    1931             :   auto search = _edge_neighbors.find(ordered_vertices);
    1932     2884395 :   if (search != _edge_neighbors.end())
    1933     2752570 :     entry = &search->second;
    1934             :   else
    1935             :   {
    1936             :     debugRay("Building edge neighbors");
    1937      131825 :     ++_results[EDGE_NEIGHBOR_BUILDS];
    1938      131825 :     entry = &_edge_neighbors
    1939      131825 :                  .emplace(ordered_vertices, std::make_pair(true, std::vector<NeighborInfo>()))
    1940             :                  .first->second;
    1941      131825 :     findEdgeNeighbors(elem,
    1942      131825 :                       ordered_vertices.first,
    1943      131825 :                       ordered_vertices.second,
    1944      131825 :                       _neighbor_set,
    1945      131825 :                       _neighbor_untested_set,
    1946      131825 :                       _neighbor_next_untested_set,
    1947      131825 :                       _neighbor_active_neighbor_children,
    1948      131825 :                       entry->second);
    1949             : 
    1950             :     bool all_same_edge = true;
    1951      646538 :     for (auto & neighbor_info : entry->second)
    1952             :     {
    1953             :       traceAssert(neighbor_info._lower_bound <= neighbor_info._upper_bound,
    1954             :                   "Bound order incorrect");
    1955             : 
    1956             :       // Fill the side normals
    1957     1544095 :       for (MooseIndex(neighbor_info._sides.size()) i = 0; i < neighbor_info._sides.size(); ++i)
    1958     1029382 :         neighbor_info._side_normals[i] =
    1959     1029382 :             _study.getSideNormal(neighbor_info._elem, neighbor_info._sides[i], _tid);
    1960             : 
    1961             :       // See if the bounds are the same as the target edge
    1962      514713 :       if (neighbor_info._lower_bound != 0 || neighbor_info._upper_bound != 1)
    1963             :         all_same_edge = false;
    1964             :     }
    1965      131825 :     entry->first = all_same_edge;
    1966             :   }
    1967             : 
    1968             :   // Means that all neighbors are not on the exact same edge, so we must
    1969             :   // validate/invalidate based on if the neighbor's edge contains our point
    1970     2884395 :   if (!entry->first)
    1971             :   {
    1972             :     const auto edge_length =
    1973         596 :         ((Point)*ordered_vertices.first - (Point)*ordered_vertices.second).norm();
    1974         596 :     const auto point_location = ((Point)*ordered_vertices.first - point).norm() / edge_length;
    1975        4282 :     for (auto & info : entry->second)
    1976        7372 :       info._valid = (info._lower_bound - TRACE_TOLERANCE) < point_location &&
    1977        3074 :                     point_location < (info._upper_bound + TRACE_TOLERANCE);
    1978             :   }
    1979             : 
    1980     2884395 :   return entry->second;
    1981             : }
    1982             : 
    1983             : const std::vector<NeighborInfo> &
    1984     2884395 : TraceRay::getEdgeNeighbors(const Elem * elem,
    1985             :                            const std::pair<unsigned short, unsigned short> & vertices,
    1986             :                            const Point & point)
    1987             : {
    1988             :   debugRay("Called getEdgeNeighbors(), local index version with:");
    1989             :   debugRay("  vertices.first = ", vertices.first);
    1990             :   debugRay("  vertices.second = ", vertices.second);
    1991             :   traceAssert(vertices.first < elem->n_vertices(),
    1992             :               "Invalid vertex with ray " + std::to_string((*_current_ray)->id()));
    1993             :   traceAssert(vertices.second < elem->n_vertices(), "Invalid vertex");
    1994             : 
    1995     2884395 :   return getEdgeNeighbors(
    1996     2884395 :       elem, std::make_pair(elem->node_ptr(vertices.first), elem->node_ptr(vertices.second)), point);
    1997             : }
    1998             : 
    1999             : const std::vector<NeighborInfo> &
    2000     3793814 : TraceRay::getNeighbors(const Elem * elem, const ElemExtrema & extrema, const Point & point)
    2001             : {
    2002     3793814 :   if (!extrema.atExtrema())
    2003           0 :     return getPointNeighbors(elem, point);
    2004             :   if (extrema.atVertex())
    2005      909419 :     return getVertexNeighbors(elem, extrema.vertex());
    2006     2884395 :   return getEdgeNeighbors(elem, extrema.edgeVertices(), point);
    2007             : }
    2008             : 
    2009             : const std::vector<NeighborInfo> &
    2010          22 : TraceRay::getPointNeighbors(const Elem * elem, const Point & point)
    2011             : {
    2012             :   traceAssert(elem, "Invalid elem");
    2013             : 
    2014             :   debugRay("Called getPointNeighbors()");
    2015             :   debugRay(" elem = ", elem->id());
    2016             :   debugRay(" point = ", point);
    2017             : 
    2018          22 :   ++_results[POINT_NEIGHBOR_BUILDS];
    2019          22 :   _point_neighbor_helper.clear();
    2020             : 
    2021          22 :   findPointNeighbors(elem,
    2022             :                      point,
    2023          22 :                      _neighbor_set,
    2024          22 :                      _neighbor_untested_set,
    2025          22 :                      _neighbor_next_untested_set,
    2026          22 :                      _neighbor_active_neighbor_children,
    2027             :                      _point_neighbor_helper);
    2028             : 
    2029             :   // Fill the side normals
    2030          44 :   for (auto & neighbor_info : _point_neighbor_helper)
    2031          44 :     for (MooseIndex(neighbor_info._sides.size()) i = 0; i < neighbor_info._sides.size(); ++i)
    2032          22 :       neighbor_info._side_normals[i] =
    2033          22 :           _study.getSideNormal(neighbor_info._elem, neighbor_info._sides[i], _tid);
    2034             : 
    2035          22 :   return _point_neighbor_helper;
    2036             : }
    2037             : 
    2038             : void
    2039    23616445 : TraceRay::storeExitsElemResult(const TraceRay::ExitsElemResult result)
    2040             : {
    2041    23616445 :   if (result == HIT_FACE)
    2042    20350947 :     ++_results[FACE_HITS];
    2043     3265498 :   else if (result == HIT_VERTEX)
    2044      812794 :     ++_results[VERTEX_HITS];
    2045     2452704 :   else if (result == HIT_EDGE)
    2046     2452704 :     ++_results[EDGE_HITS];
    2047             :   else
    2048           0 :     mooseError("Should not call storeExitsElemResult() with result ", result);
    2049    23616445 : }
    2050             : 
    2051             : void
    2052    22560690 : TraceRay::onSegment(const std::shared_ptr<Ray> & ray)
    2053             : {
    2054             :   traceAssert((*_current_ray)->currentElem() == _current_elem, "Ray currentElem() incorrect");
    2055             :   traceAssert((*_current_ray)->currentPoint() == _intersection_point,
    2056             :               "Ray currentPoint() incorrect");
    2057             :   traceAssert((*_current_ray)->currentIncomingSide() == _incoming_side,
    2058             :               "Ray currentIncomingSide() incorrect");
    2059             : #ifndef NDEBUG
    2060             :   if (_study.verifyTraceIntersections())
    2061             :   {
    2062             :     if (_current_elem->has_affine_map())
    2063             :       traceAssert(_current_elem->contains_point(_incoming_point),
    2064             :                   "_current_elem does not contain incoming point");
    2065             : 
    2066             :     if (_intersected_side != RayTracingCommon::invalid_side &&
    2067             :         !_study.sideIsNonPlanar(_current_elem, _intersected_side))
    2068             :     {
    2069             :       traceAssert(_elem_side_builder(*_current_elem, _intersected_side)
    2070             :                       .close_to_point(_intersection_point, LOOSE_TRACE_TOLERANCE),
    2071             :                   "Intersected point is not on intersected side");
    2072             :       traceAssert(!_study.sideIsIncoming(
    2073             :                       _current_elem, _intersected_side, (*_current_ray)->direction(), _tid),
    2074             :                   "Intersected side is not outgoing");
    2075             :     }
    2076             :     if (_incoming_side != RayTracingCommon::invalid_side &&
    2077             :         !_study.sideIsNonPlanar(_current_elem, _incoming_side))
    2078             :     {
    2079             :       traceAssert(_elem_side_builder(*_current_elem, _incoming_side)
    2080             :                       .close_to_point(_incoming_point, LOOSE_TRACE_TOLERANCE),
    2081             :                   "Incoming point is not on incoming side");
    2082             :       if (ray->intersections() != 0 && ray->maxDistance() != 0)
    2083             :         traceAssert(_study.sideIsIncoming(
    2084             :                         _current_elem, _incoming_side, (*_current_ray)->direction(), _tid),
    2085             :                     "Incoming side is not incoming");
    2086             :     }
    2087             :   }
    2088             : #endif
    2089             :   traceAssert(MooseUtils::absoluteFuzzyEqual(_intersection_distance,
    2090             :                                              (_intersection_point - _incoming_point).norm()),
    2091             :               "_intersection_distance is incorrect");
    2092             :   traceAssert(_current_subdomain_id == _current_elem->subdomain_id(), "Subdomain incorrect");
    2093             :   traceAssert(MooseUtils::absoluteFuzzyEqual((_incoming_point - _intersection_point).norm(),
    2094             :                                              _intersection_distance),
    2095             :               "Invalid intersection distance");
    2096             : 
    2097    22560690 :   _study.reinitSegment(
    2098    22560690 :       _current_elem, _incoming_point, _intersection_point, _intersection_distance, _tid);
    2099             : 
    2100    22560690 :   const auto & rks = _study.currentRayKernels(_tid);
    2101    45130632 :   for (auto & rk : rks)
    2102             :   {
    2103    22569954 :     rk->onSegment();
    2104    22569942 :     postRayTracingObject(ray, rk);
    2105             :   }
    2106    22560678 : }
    2107             : 
    2108             : void
    2109      973271 : TraceRay::onBoundary(const std::shared_ptr<Ray> & ray, const bool external)
    2110             : {
    2111             :   traceAssert(ray->currentPoint().absolute_fuzzy_equals(_intersection_point),
    2112             :               "Ray currentPoint() not set before onBoundary()");
    2113             : 
    2114             :   // Get the RayBCs on bnd_elems
    2115      973271 :   _study.getRayBCs(_on_boundary_ray_bcs, _boundary_elems, _tid, ray->id());
    2116             : 
    2117             :   // Store this information temprorarily because we are going to change it as we
    2118             :   // apply each boundary condition
    2119      973271 :   const auto old_current_elem = _current_elem;
    2120      973271 :   const auto old_intersected_side = _intersected_side;
    2121      973271 :   const auto old_intersected_extrema = _intersected_extrema;
    2122      973271 :   const auto old_subdomain_id = _current_subdomain_id;
    2123             : 
    2124             :   // For each RayBC we found, apply it on the boundaries that we need
    2125     1962950 :   for (RayBoundaryConditionBase * rbc : _on_boundary_ray_bcs)
    2126             :   {
    2127             :     // First, find the boundaries this RayBC is valid on that are also in _boundary_elems.
    2128             :     // We do this ahead of time so that we can pass in to RayBC::apply if the same
    2129             :     // boundary condition is being appled multiple times on different boundaries.
    2130             :     // This is useful in situations like reflection where multiple reflections are
    2131             :     // necessary at a corner to perfectly reflect.
    2132      989689 :     _on_boundary_apply_index.clear();
    2133     2053953 :     for (MooseIndex(_boundary_elems.size()) bnd_elems_i = 0; bnd_elems_i < _boundary_elems.size();
    2134             :          ++bnd_elems_i)
    2135     1064264 :       if (rbc->hasBoundary(_boundary_elems[bnd_elems_i].bnd_id))
    2136     1064214 :         _on_boundary_apply_index.push_back(bnd_elems_i);
    2137             : 
    2138             :     traceAssert(!_on_boundary_apply_index.empty(), "Must not be empty");
    2139             : 
    2140             :     // Apply the RayBC on each of the relevant boundary elements
    2141     2053893 :     for (const auto bnd_elems_i : _on_boundary_apply_index)
    2142             :     {
    2143             :       auto & bnd_elem = _boundary_elems[bnd_elems_i];
    2144             : 
    2145             :       debugRay("Calling ",
    2146             :                rbc->type(),
    2147             :                "::onBoundary for \"",
    2148             :                rbc->name(),
    2149             :                "\" on elem ",
    2150             :                bnd_elem.elem->id(),
    2151             :                " and side ",
    2152             :                bnd_elem.side,
    2153             :                " for bnd_id ",
    2154             :                bnd_elem.bnd_id);
    2155             : 
    2156     1064214 :       _current_elem = bnd_elem.elem;
    2157     1064214 :       _current_bnd_id = bnd_elem.bnd_id;
    2158     1064214 :       _intersected_side = bnd_elem.side;
    2159             :       _intersected_extrema = bnd_elem.extrema;
    2160     1064214 :       _current_subdomain_id = _current_elem->subdomain_id();
    2161             : 
    2162     1064214 :       rbc->onBoundary(_on_boundary_apply_index.size());
    2163     1064204 :       postRayTracingObject(ray, rbc);
    2164             :     }
    2165             :   }
    2166             : 
    2167             :   // Set this info back now that we're done applying BCs
    2168      973261 :   _current_elem = old_current_elem;
    2169      973261 :   _intersected_side = old_intersected_side;
    2170             :   _intersected_extrema = old_intersected_extrema;
    2171      973261 :   _current_bnd_id = BoundaryInfo::invalid_id;
    2172      973261 :   _current_subdomain_id = old_subdomain_id;
    2173             : 
    2174             :   // When on an external boundary, the Ray must have been changed or killed.
    2175             :   // Otherwise, we don't know what to do with it now! If this didn't happen,
    2176             :   // output a detailed error message.
    2177      973261 :   if (external && !ray->trajectoryChanged() && ray->shouldContinue())
    2178             :   {
    2179           8 :     std::stringstream oss;
    2180           8 :     oss << "Don't know what to do with a Ray after it hit an external\n";
    2181           8 :     oss << "boundary at point " << _intersection_point << "!\n\n";
    2182           8 :     oss << "When hitting an external RayBC, a Ray must either:\n";
    2183           8 :     oss << "  Be killed by a RayBC\n";
    2184           8 :     oss << "  Have its trajectory changed by the RayBC\n";
    2185           8 :     oss << "by at least one of the executed RayBCs.\n\n";
    2186           8 :     oss << "You need to either:\n";
    2187           8 :     oss << "  Kill/change the Ray sooner with RayKernels, internal RayBCs, or a max distance\n";
    2188           8 :     oss << "  Kill/change the Ray on the boundary with a RayBC\n\n";
    2189           8 :     if (!_on_boundary_ray_bcs.empty())
    2190             :     {
    2191           6 :       oss << "RayBCs executed that did not kill or change the Ray:\n";
    2192          12 :       for (const RayBoundaryConditionBase * rbc : _on_boundary_ray_bcs)
    2193          14 :         for (const auto & bnd_elem : _boundary_elems)
    2194           8 :           if (rbc->hasBoundary(bnd_elem.bnd_id))
    2195          18 :             oss << "  " << rbc->typeAndName() << " on boundary " << bnd_elem.bnd_id << " ("
    2196          12 :                 << _mesh.getBoundaryName(bnd_elem.bnd_id) << ")\n";
    2197           6 :       oss << "\n";
    2198             :     }
    2199             :     bool output_header = false;
    2200          20 :     for (std::size_t i = 0; i < _boundary_elems.size(); ++i)
    2201             :     {
    2202          12 :       const auto bnd_id = _boundary_elems[i].bnd_id;
    2203             :       bool found = false;
    2204          14 :       for (const RayBoundaryConditionBase * rbc : _on_boundary_ray_bcs)
    2205           8 :         if (rbc->hasBoundary(bnd_id))
    2206             :         {
    2207             :           found = true;
    2208             :           break;
    2209             :         }
    2210             : 
    2211          12 :       if (!found)
    2212             :       {
    2213           6 :         if (!output_header)
    2214             :         {
    2215           4 :           oss << "Boundaries that did not have any RayBCs:\n";
    2216             :           output_header = true;
    2217             :         }
    2218          12 :         oss << "  " << bnd_id << " (" << _mesh.getBoundaryName(bnd_id) << ")\n";
    2219             :       }
    2220             :     }
    2221             : 
    2222           8 :     failTrace(oss.str(), _study.tolerateFailure(), __LINE__);
    2223           4 :   }
    2224      973257 : }
    2225             : 
    2226             : Real
    2227    31429648 : TraceRay::subdomainHmax(const Elem * elem) const
    2228             : {
    2229             :   const auto subdomain_id = elem->subdomain_id();
    2230    31429648 :   return subdomain_id == _current_subdomain_id ? _current_subdomain_hmax
    2231        1449 :                                                : _study.subdomainHmax(subdomain_id);
    2232             : }
    2233             : 
    2234             : void
    2235    23634146 : TraceRay::postRayTracingObject(const std::shared_ptr<Ray> & ray, const RayTracingObject * rto)
    2236             : {
    2237    23634146 :   if (!ray->shouldContinue())
    2238             :   {
    2239     2719014 :     if (_should_continue)
    2240      938697 :       _should_continue = false;
    2241             :   }
    2242    20915132 :   else if (!_should_continue)
    2243           0 :     failTrace(rto->typeAndName() +
    2244           0 :                   " set a Ray to continue that was previously set to not continue.\n\n" +
    2245             :                   "Once a Ray has been set to not continue, its continue status cannot change.",
    2246             :               /* warning = */ false,
    2247             :               __LINE__);
    2248             : 
    2249    23634146 :   if (!_should_continue && ray->trajectoryChanged())
    2250           0 :     failTrace(rto->typeAndName() +
    2251           0 :                   " changed the trajectory of a Ray that was set to not continue,\n" +
    2252             :                   "or set a Ray whose trajectory was changed to not continue.",
    2253             :               /* warning = */ false,
    2254             :               __LINE__);
    2255    23634146 : }

Generated by: LCOV version 1.14