LCOV - code coverage report
Current view: top level - src/raytracing - TraceRayTools.C (source / functions) Hit Total Coverage
Test: idaholab/moose ray_tracing: #31405 (292dce) with base fef103 Lines: 207 222 93.2 %
Date: 2025-09-04 07:56:07 Functions: 36 36 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 "TraceRayTools.h"
      11             : 
      12             : // MOOSE includes
      13             : #include "MooseTypes.h"
      14             : #include "MooseUtils.h"
      15             : 
      16             : // libMesh includes
      17             : #include "libmesh/cell_hex8.h"
      18             : #include "libmesh/cell_hex20.h"
      19             : #include "libmesh/cell_hex27.h"
      20             : #include "libmesh/cell_prism6.h"
      21             : #include "libmesh/cell_prism15.h"
      22             : #include "libmesh/cell_prism18.h"
      23             : #include "libmesh/cell_pyramid5.h"
      24             : #include "libmesh/cell_pyramid13.h"
      25             : #include "libmesh/cell_pyramid14.h"
      26             : #include "libmesh/cell_tet4.h"
      27             : #include "libmesh/cell_tet10.h"
      28             : #include "libmesh/cell_tet14.h"
      29             : #include "libmesh/edge_edge2.h"
      30             : #include "libmesh/enum_to_string.h"
      31             : #include "libmesh/face_quad4.h"
      32             : #include "libmesh/face_tri3.h"
      33             : #include "libmesh/mesh.h"
      34             : #include "libmesh/remote_elem.h"
      35             : #include "libmesh/tensor_value.h"
      36             : 
      37             : using namespace libMesh;
      38             : 
      39             : namespace TraceRayTools
      40             : {
      41             : 
      42             : const std::set<int> TRACEABLE_ELEMTYPES = {
      43             :     HEX8, HEX20, HEX27, QUAD4, QUAD8,    QUAD9,     TET4,      TET10,  TET14,   TRI3,   TRI6,
      44             :     TRI7, EDGE2, EDGE3, EDGE4, PYRAMID5, PYRAMID13, PYRAMID14, PRISM6, PRISM15, PRISM18};
      45             : const std::set<int> ADAPTIVITY_TRACEABLE_ELEMTYPES = {QUAD4, HEX8, TRI3, TET4, EDGE2};
      46             : 
      47             : bool
      48     3084612 : lineLineIntersect2D(const Point & start,
      49             :                     const Point & direction,
      50             :                     const Real length,
      51             :                     const Point & v0,
      52             :                     const Point & v1,
      53             :                     Point & intersection_point,
      54             :                     Real & intersection_distance,
      55             :                     SegmentVertices & segment_vertex
      56             : #ifdef DEBUG_RAY_INTERSECTIONS
      57             :                     ,
      58             :                     const bool debug
      59             : #endif
      60             : )
      61             : 
      62             : {
      63             :   // TODO: consider using hmax scaling here
      64             :   mooseAssert(segment_vertex == SEGMENT_VERTEX_NONE, "Vertex should be none");
      65             :   debugRaySimple("Called lineLineIntersect2D()");
      66             :   debugRaySimple("  start = ", start);
      67             :   debugRaySimple("  direction = ", direction);
      68             :   debugRaySimple("  length = ", length);
      69             :   debugRaySimple("  v0 = ", v0);
      70             :   debugRaySimple("  v1 = ", v1);
      71             : 
      72             :   const auto r = direction * length;
      73             :   const auto s = v1 - v0;
      74             : 
      75     3084612 :   const auto rxs = r(0) * s(1) - r(1) * s(0);
      76             :   debugRaySimple("  rxs = ", rxs);
      77             : 
      78             :   // Lines are parallel or colinear
      79     3084612 :   if (std::abs(rxs) < TRACE_TOLERANCE)
      80             :     return false;
      81             : 
      82             :   const auto v0mu0 = v0 - start;
      83             : 
      84     2695725 :   const auto t = (v0mu0(0) * s(1) - v0mu0(1) * s(0)) / rxs;
      85             :   debugRaySimple("  t = ", t);
      86     2695725 :   if (0 >= t + TRACE_TOLERANCE || t - TRACE_TOLERANCE > 1.0)
      87             :   {
      88             :     debugRaySimple("lineLineIntersect2D did not intersect: t out of range");
      89             :     return false;
      90             :   }
      91             : 
      92     1995229 :   const auto u = (v0mu0(0) * r(1) - v0mu0(1) * r(0)) / rxs;
      93             :   debugRaySimple("  u = ", u);
      94     1995229 :   if (0 < u + TRACE_TOLERANCE && u - TRACE_TOLERANCE <= 1.0)
      95             :   {
      96     1382509 :     intersection_point = start + r * t;
      97     1382509 :     intersection_distance = t * length;
      98             : 
      99     1382509 :     if (u < TRACE_TOLERANCE)
     100      359438 :       segment_vertex = SEGMENT_VERTEX_0;
     101     1023071 :     else if (u > 1.0 - TRACE_TOLERANCE)
     102      298531 :       segment_vertex = SEGMENT_VERTEX_1;
     103             : 
     104             :     debugRaySimple("lineLineIntersect2D intersected with:");
     105             :     debugRaySimple("  intersection_distance = ", intersection_point);
     106             :     debugRaySimple("  intersection_distance = ", intersection_distance);
     107             :     debugRaySimple("  segment_vertex = ", Utility::enum_to_string(segment_vertex));
     108             : 
     109     1382509 :     return true;
     110             :   }
     111             : 
     112             :   // Not parallel, but don't intersect
     113             :   debugRaySimple("lineLineIntersect2d() did not intersect: u out of range");
     114             :   return false;
     115             : }
     116             : 
     117             : void
     118       17104 : findPointNeighbors(
     119             :     const Elem * const elem,
     120             :     const Point & point,
     121             :     MooseUtils::StaticallyAllocatedSet<const Elem *, MAX_POINT_NEIGHBORS> & neighbor_set,
     122             :     MooseUtils::StaticallyAllocatedSet<const Elem *, MAX_POINT_NEIGHBORS> & untested_set,
     123             :     MooseUtils::StaticallyAllocatedSet<const Elem *, MAX_POINT_NEIGHBORS> & next_untested_set,
     124             :     std::vector<const Elem *> active_neighbor_children,
     125             :     std::vector<NeighborInfo> & info)
     126             : {
     127             :   mooseAssert(elem->contains_point(point), "Doesn't contain point");
     128             : 
     129       17104 :   info.clear();
     130             : 
     131             :   // Helper for avoiding extraneous allocation when building side elements
     132       17104 :   std::unique_ptr<const Elem> side_helper;
     133             : 
     134      232856 :   auto contains_point = [&point, &info, &side_helper, &elem](const Elem * const candidate)
     135             :   {
     136      232856 :     if (candidate->contains_point(point))
     137             :     {
     138             :       std::vector<unsigned short> sides;
     139      492386 :       for (const auto s : candidate->side_index_range())
     140             :       {
     141      398470 :         candidate->build_side_ptr(side_helper, s);
     142      398470 :         if (side_helper->contains_point(point))
     143      229704 :           sides.push_back(s);
     144             :       }
     145             : 
     146             :       // Dont add the local element
     147       93916 :       if (!sides.empty() && candidate != elem)
     148             :       {
     149       76812 :         info.emplace_back(candidate, std::move(sides));
     150             :         return true;
     151             :       }
     152       93916 :     }
     153             : 
     154             :     return false;
     155       17104 :   };
     156             : 
     157             :   // Fill info for the element that was passed in
     158       17104 :   contains_point(elem);
     159             : 
     160       17104 :   findNeighbors(elem,
     161             :                 neighbor_set,
     162             :                 untested_set,
     163             :                 next_untested_set,
     164             :                 active_neighbor_children,
     165             :                 contains_point);
     166             : 
     167             : #ifndef NDEBUG
     168             :   // In non-opt modes, verify that we found all of the correct neighbors
     169             :   // using the more expensive libMesh routine
     170             :   std::set<const Elem *> point_neighbors;
     171             :   elem->find_point_neighbors(point, point_neighbors);
     172             :   for (const auto & point_neighbor : point_neighbors)
     173             :     if (!neighbor_set.contains(point_neighbor) && point_neighbor != elem)
     174             :       mooseError("Missed a point neighbor");
     175             : #endif
     176       17104 : }
     177             : 
     178             : void
     179      105739 : findNodeNeighbors(
     180             :     const Elem * const elem,
     181             :     const Node * const node,
     182             :     MooseUtils::StaticallyAllocatedSet<const Elem *, MAX_POINT_NEIGHBORS> & neighbor_set,
     183             :     MooseUtils::StaticallyAllocatedSet<const Elem *, MAX_POINT_NEIGHBORS> & untested_set,
     184             :     MooseUtils::StaticallyAllocatedSet<const Elem *, MAX_POINT_NEIGHBORS> & next_untested_set,
     185             :     std::vector<const Elem *> active_neighbor_children,
     186             :     std::vector<NeighborInfo> & info)
     187             : {
     188             :   mooseAssert(elem->get_node_index(node) != libMesh::invalid_uint, "Doesn't contain node");
     189             : 
     190      105739 :   info.clear();
     191             : 
     192             :   // Helper for avoiding extraneous allocations when building side elements
     193      105739 :   std::unique_ptr<const Elem> side_helper;
     194             : 
     195     1793872 :   auto contains_node = [&node, &elem, &info, &side_helper](const Elem * const candidate)
     196             :   {
     197             :     // Candidate has this node and it is a vertex - add sides that contain said node
     198     1793872 :     const auto n = candidate->get_node_index(node);
     199     1793872 :     if (n != invalid_uint && candidate->is_vertex(n))
     200             :     {
     201             :       std::vector<unsigned short> sides;
     202     4119134 :       for (const auto s : candidate->side_index_range())
     203     3410002 :         if (candidate->is_node_on_side(n, s))
     204     2135910 :           sides.push_back(s);
     205             : 
     206      709132 :       if (sides.empty())
     207           0 :         mooseError("Failed to find a side containing node");
     208             : 
     209      709132 :       info.emplace_back(candidate, std::move(sides));
     210             :       return true;
     211      709132 :     }
     212             :     // In the case of a less refined candidate, the node can be a hanging node. The candidate
     213             :     // will only ever have the hanging node if it is less refined.
     214     1084740 :     if (candidate->level() < elem->level() && candidate->contains_point(*node))
     215             :     {
     216             :       std::vector<unsigned short> sides;
     217        6368 :       for (const auto s : candidate->side_index_range())
     218             :       {
     219        5302 :         candidate->build_side_ptr(side_helper, s);
     220        5302 :         if (side_helper->contains_point(*node))
     221        1356 :           sides.push_back(s);
     222             :       }
     223             : 
     224        1066 :       if (!sides.empty())
     225             :       {
     226        1066 :         info.emplace_back(candidate, std::move(sides));
     227             :         return true;
     228             :       }
     229        1066 :     }
     230             : 
     231             :     return false;
     232      105739 :   };
     233             : 
     234             :   // Fill info for the element that was passed in
     235      105739 :   contains_node(elem);
     236             : 
     237      105739 :   findNeighbors(
     238             :       elem, neighbor_set, untested_set, next_untested_set, active_neighbor_children, contains_node);
     239             : 
     240             : #ifndef NDEBUG
     241             :   // In non-opt modes, verify that we found all of the correct neighbors
     242             :   // using the more expensive libMesh routine
     243             :   std::set<const Elem *> point_neighbors;
     244             :   elem->find_point_neighbors(*node, point_neighbors);
     245             :   for (const auto & point_neighbor : point_neighbors)
     246             :     for (const auto & neighbor_node : point_neighbor->node_ref_range())
     247             :       if (node == &neighbor_node && !neighbor_set.contains(point_neighbor) &&
     248             :           point_neighbor != elem)
     249             :         mooseError("Missed a node neighbor");
     250             : #endif
     251      105739 : }
     252             : 
     253             : void
     254      211605 : findEdgeNeighbors(
     255             :     const Elem * const elem,
     256             :     const Node * const node1,
     257             :     const Node * const node2,
     258             :     MooseUtils::StaticallyAllocatedSet<const Elem *, MAX_POINT_NEIGHBORS> & neighbor_set,
     259             :     MooseUtils::StaticallyAllocatedSet<const Elem *, MAX_POINT_NEIGHBORS> & untested_set,
     260             :     MooseUtils::StaticallyAllocatedSet<const Elem *, MAX_POINT_NEIGHBORS> & next_untested_set,
     261             :     std::vector<const Elem *> active_neighbor_children,
     262             :     std::vector<NeighborInfo> & info)
     263             : {
     264             :   mooseAssert(elem->get_node_index(node1) != libMesh::invalid_uint, "Doesn't contain node");
     265             :   mooseAssert(elem->get_node_index(node2) != libMesh::invalid_uint, "Doesn't contain node");
     266             : 
     267      211605 :   info.clear();
     268             : 
     269             :   // The length for this edge used for checking if a point is contained within said edge
     270      211605 :   const Real edge_length = ((Point)*node1 - (Point)*node2).norm();
     271             : 
     272             :   // Lambda that returns whether or not a candidate element contains an edge that is within the
     273             :   // target edge defined by node1 and node2. Also fills "info" if a match is found
     274     2897955 :   auto within_edge = [&elem, &node1, &node2, &edge_length, &info](const Elem * const candidate)
     275             :   {
     276     2897955 :     switch (candidate->type())
     277             :     {
     278      283564 :       case HEX8:
     279      283564 :         return findEdgeNeighborsWithinEdgeInternal<Hex8>(
     280      283564 :             candidate, elem, node1, node2, edge_length, info);
     281      275726 :       case TET4:
     282      275726 :         return findEdgeNeighborsWithinEdgeInternal<Tet4>(
     283      275726 :             candidate, elem, node1, node2, edge_length, info);
     284      396728 :       case PYRAMID5:
     285      396728 :         return findEdgeNeighborsWithinEdgeInternal<Pyramid5>(
     286      396728 :             candidate, elem, node1, node2, edge_length, info);
     287      120409 :       case PRISM6:
     288      120409 :         return findEdgeNeighborsWithinEdgeInternal<Prism6>(
     289      120409 :             candidate, elem, node1, node2, edge_length, info);
     290      124108 :       case HEX20:
     291      124108 :         return findEdgeNeighborsWithinEdgeInternal<Hex20>(
     292      124108 :             candidate, elem, node1, node2, edge_length, info);
     293      124091 :       case HEX27:
     294      124091 :         return findEdgeNeighborsWithinEdgeInternal<Hex27>(
     295      124091 :             candidate, elem, node1, node2, edge_length, info);
     296      271006 :       case TET10:
     297      271006 :         return findEdgeNeighborsWithinEdgeInternal<Tet10>(
     298      271006 :             candidate, elem, node1, node2, edge_length, info);
     299      270876 :       case TET14:
     300      270876 :         return findEdgeNeighborsWithinEdgeInternal<Tet14>(
     301      270876 :             candidate, elem, node1, node2, edge_length, info);
     302      395462 :       case PYRAMID13:
     303      395462 :         return findEdgeNeighborsWithinEdgeInternal<Pyramid13>(
     304      395462 :             candidate, elem, node1, node2, edge_length, info);
     305      395242 :       case PYRAMID14:
     306      395242 :         return findEdgeNeighborsWithinEdgeInternal<Pyramid14>(
     307      395242 :             candidate, elem, node1, node2, edge_length, info);
     308      120413 :       case PRISM15:
     309      120413 :         return findEdgeNeighborsWithinEdgeInternal<Prism15>(
     310      120413 :             candidate, elem, node1, node2, edge_length, info);
     311      120330 :       case PRISM18:
     312      120330 :         return findEdgeNeighborsWithinEdgeInternal<Prism18>(
     313      120330 :             candidate, elem, node1, node2, edge_length, info);
     314           0 :       default:
     315           0 :         mooseError("Element type ",
     316           0 :                    Utility::enum_to_string(candidate->type()),
     317             :                    " not supported in TraceRayTools::findEdgeNeighbors()");
     318             :     }
     319      211605 :   };
     320             : 
     321             :   // Fill info for the element that was passed in
     322      211605 :   within_edge(elem);
     323             : 
     324      211605 :   findNeighbors(
     325             :       elem, neighbor_set, untested_set, next_untested_set, active_neighbor_children, within_edge);
     326      211605 : }
     327             : 
     328             : const Elem *
     329        9273 : childContainingPointOnSide(const Elem * elem, const Point & point, const unsigned short side)
     330             : {
     331             :   mooseAssert(!elem->active(), "Should be inactive");
     332             :   mooseAssert(elem->side_ptr(side)->contains_point(point), "Side should contain point");
     333             : 
     334       21958 :   for (unsigned int c = 0; c < elem->n_children(); ++c)
     335             :   {
     336       21958 :     if (!elem->is_child_on_side(c, side))
     337        6615 :       continue;
     338             : 
     339             :     const auto child = elem->child_ptr(c);
     340             :     // Experience shows that we need to loosen this tolerance just a little
     341             :     // The default is libMesh::TOLERANCE = 1e-6
     342       15343 :     if (child->close_to_point(point, 5.e-5))
     343             :     {
     344             :       if (child->active())
     345             :         return child;
     346             :       else
     347           0 :         return childContainingPointOnSide(child, point, side);
     348             :     }
     349             :   }
     350             : 
     351           0 :   mooseError("Failed to find child containing point on side");
     352             : }
     353             : 
     354             : const Elem *
     355      189283 : getActiveNeighbor(const Elem * elem, const unsigned short side, const Point & point)
     356             : {
     357      189283 :   const auto neighbor = elem->neighbor_ptr(side);
     358      189283 :   if (!neighbor || neighbor->active())
     359             :     return neighbor;
     360             : 
     361             :   // There is adaptivity... need to find the active child that contains the point
     362        9273 :   const auto neighbor_side = neighbor->which_neighbor_am_i(elem);
     363        9273 :   return childContainingPointOnSide(neighbor, point, neighbor_side);
     364             : }
     365             : 
     366             : bool
     367   239549538 : intersectTriangle(const Point & start,
     368             :                   const Point & direction,
     369             :                   const Elem * const elem,
     370             :                   const unsigned short v0,
     371             :                   const unsigned short v1,
     372             :                   const unsigned short v2,
     373             :                   Real & intersection_distance,
     374             :                   ElemExtrema & intersected_extrema,
     375             :                   const Real hmax
     376             : #ifdef DEBUG_RAY_INTERSECTIONS
     377             :                   ,
     378             :                   const bool debug
     379             : #endif
     380             : )
     381             : {
     382             :   debugRaySimple("intersectTriangle() called:");
     383             :   debugRaySimple("  start = ", start);
     384             :   debugRaySimple("  direction = ", direction);
     385             :   debugRaySimple("  elem->id() = ", elem->id());
     386             :   debugRaySimple("  v0 = ", v0, " at ", elem->point(v0));
     387             :   debugRaySimple("  v1 = ", v1, " at ", elem->point(v1));
     388             :   debugRaySimple("  v2 = ", v2, " at ", elem->point(v2));
     389             :   debugRaySimple("  hmax = ", hmax);
     390             :   mooseAssert(elem->is_vertex(v0), "Not a vertex");
     391             :   mooseAssert(elem->is_vertex(v1), "Not a vertex");
     392             :   mooseAssert(elem->is_vertex(v2), "Not a vertex");
     393             : 
     394             :   // We are scaling the whole element (start, v0, v1, v2) by 1 / hmax as an alternative to scaling
     395             :   // the tolerance by hmax. If an intersection is found, the resulting intersection distance is
     396             :   // then scaled by hmax to reverse the original scaling.
     397   239549538 :   const auto inv_hmax = 1.0 / hmax;
     398             : 
     399             :   const auto & v0_point = elem->point(v0);
     400             : 
     401             :   const auto edge1 = (elem->point(v1) - v0_point) * inv_hmax;
     402             :   const auto edge2 = (elem->point(v2) - v0_point) * inv_hmax;
     403             : 
     404   239549538 :   const auto pvec = direction.cross(edge2);
     405             : 
     406             :   auto det = edge1 * pvec;
     407             :   debugRaySimple("  det = ", det);
     408   239549538 :   if (det < TRACE_TOLERANCE)
     409             :   {
     410             :     debugRaySimple("intersectTriangle() did not intersect: det < tol");
     411             :     return false;
     412             :   }
     413             : 
     414             :   const auto tvec = (start - v0_point) * inv_hmax;
     415             :   const auto u = tvec * pvec;
     416             :   debugRaySimple("  u = ", u);
     417             :   debugRaySimple("  u / det = ", u / det);
     418   124012804 :   if (u < -TRACE_TOLERANCE || u > det + TRACE_TOLERANCE)
     419             :   {
     420             :     debugRaySimple("intersectTriangle() did not intersect: u out of range");
     421             :     return false;
     422             :   }
     423             : 
     424    70369057 :   const auto qvec = tvec.cross(edge1);
     425             :   const auto v = direction * qvec;
     426             :   debugRaySimple("  v = ", v);
     427             :   debugRaySimple("  v / det = ", v / det);
     428             :   debugRaySimple("  (u + v) / det = ", (u + v) / det);
     429    70369057 :   if (v < -TRACE_TOLERANCE || u + v > det + TRACE_TOLERANCE)
     430             :   {
     431             :     debugRaySimple("intersectTriangle() did not intersect: v out of range");
     432             :     return false;
     433             :   }
     434             : 
     435    52293630 :   const auto possible_distance = (edge2 * qvec) / det;
     436             :   debugRaySimple("  possible_distance = ", possible_distance);
     437    52293630 :   if (possible_distance <= TRACE_TOLERANCE)
     438             :   {
     439             :     debugRaySimple("intersectTriangle() did not intersect: distance too small");
     440             :     return false;
     441             :   }
     442             : 
     443             :   // Recall that the element was scaled by (1 / hmax), reverse this scaling by
     444             :   // scaling the intersection distance by hmax
     445    40724078 :   intersection_distance = possible_distance * hmax;
     446             : 
     447             :   // Here, u and v aren't truly u and v. The actual u and v are obtained with:
     448             :   // u = u / det and v = v / det -> move det to the RHS to avoid division
     449    40724078 :   if (u < TRACE_TOLERANCE * det)
     450             :   {
     451     3861991 :     if (v < TRACE_TOLERANCE * det) // u = 0, v = 0
     452             :       intersected_extrema.setVertex(v0);
     453     2668717 :     else if (v > (1.0 - TRACE_TOLERANCE) * det) // u = 0, v = 1
     454             :       intersected_extrema.setVertex(v2);
     455             :     else // u = 0
     456             :       intersected_extrema.setEdge(v0, v2);
     457             :   }
     458    36862087 :   else if (v < TRACE_TOLERANCE * det)
     459             :   {
     460     4156255 :     if (u > (1.0 - TRACE_TOLERANCE) * det) // u = 1, v = 0
     461             :       intersected_extrema.setVertex(v1);
     462             :     else // v = 0
     463             :       intersected_extrema.setEdge(v0, v1);
     464             :   }
     465    32705832 :   else if ((u + v > (1.0 - TRACE_TOLERANCE) * det)) // u + v = 1
     466             :     intersected_extrema.setEdge(v1, v2);
     467             : 
     468             :   debugRaySimple("intersectTriangle() intersected with:");
     469             :   debugRaySimple("  intersection_distance = ", intersection_distance);
     470             :   debugRaySimple("  intersected_extrema = ", intersected_extrema);
     471             : 
     472             :   return true;
     473             : }
     474             : 
     475             : bool
     476    70477995 : intersectQuad(const Point & start,
     477             :               const Point & direction,
     478             :               const Elem * const elem,
     479             :               const unsigned short v00,
     480             :               const unsigned short v10,
     481             :               const unsigned short v11,
     482             :               const unsigned short v01,
     483             :               Real & intersection_distance,
     484             :               ElemExtrema & intersected_extrema,
     485             :               const Real hmax
     486             : #ifdef DEBUG_RAY_INTERSECTIONS
     487             :               ,
     488             :               const bool debug
     489             : #endif
     490             : )
     491             : {
     492             :   mooseAssert(intersected_extrema.isInvalid(), "Should be invalid");
     493             :   debugRaySimple("intersectQuad() called:");
     494             :   debugRaySimple("  start = ", start);
     495             :   debugRaySimple("  direction = ", direction);
     496             :   debugRaySimple("  elem->id() = ", elem->id());
     497             :   debugRaySimple("  v00 = ", v00, " at ", elem->point(v00));
     498             :   debugRaySimple("  v10 = ", v10, " at ", elem->point(v10));
     499             :   debugRaySimple("  v11 = ", v11, " at ", elem->point(v11));
     500             :   debugRaySimple("  v01 = ", v01, " at ", elem->point(v01));
     501             : 
     502             :   // NOTE discovered by @GiudGiud: In the case that you have
     503             :   // a glancing intersection (the direction is within the plane
     504             :   // of the face), you could possibly miss a further intersection
     505             :   // on the second triangle. In reality, this should not be
     506             :   // a problem because we check all sides of the element, so
     507             :   // we would find a further intersection in the future.
     508             : 
     509             :   // First check the triangle contained by v00, v10, v11
     510    70477995 :   bool intersects = intersectTriangle(start,
     511             :                                       direction,
     512             :                                       elem,
     513             :                                       v00,
     514             :                                       v10,
     515             :                                       v11,
     516             :                                       intersection_distance,
     517             :                                       intersected_extrema,
     518             :                                       hmax
     519             : #ifdef DEBUG_RAY_INTERSECTIONS
     520             :                                       ,
     521             :                                       debug
     522             : #endif
     523             :   );
     524             :   // If no intersection, check the triangle contained by v11, v01, v00
     525    70477995 :   if (!intersects)
     526    62171726 :     intersects = intersectTriangle(start,
     527             :                                    direction,
     528             :                                    elem,
     529             :                                    v11,
     530             :                                    v01,
     531             :                                    v00,
     532             :                                    intersection_distance,
     533             :                                    intersected_extrema,
     534             :                                    hmax
     535             : #ifdef DEBUG_RAY_INTERSECTIONS
     536             :                                    ,
     537             :                                    debug
     538             : #endif
     539             :     );
     540             : 
     541             :   // Because we split the quad into two triangles, we could intersect the edge v00 - v11. However,
     542             :   // that really isn't an edge - it's a diagonal across the quad. If we intersect this edge, be sure
     543             :   // to invalidate it
     544    62171726 :   if (intersects && intersected_extrema.atEdge(v00, v11))
     545             :     intersected_extrema.invalidate();
     546             : 
     547    70477995 :   return intersects;
     548             : }
     549             : 
     550             : bool
     551      456868 : isTraceableElem(const Elem * elem)
     552             : {
     553      456868 :   return TRACEABLE_ELEMTYPES.count(elem->type());
     554             : }
     555             : 
     556             : bool
     557        5670 : isAdaptivityTraceableElem(const Elem * elem)
     558             : {
     559        5670 :   return ADAPTIVITY_TRACEABLE_ELEMTYPES.count(elem->type());
     560             : }
     561             : 
     562             : unsigned short
     563        5987 : atVertex(const Elem * elem, const Point & point)
     564             : {
     565       19025 :   for (unsigned int v = 0; v < elem->n_vertices(); ++v)
     566       18161 :     if (elem->point(v).absolute_fuzzy_equals(point, TRACE_TOLERANCE))
     567        5123 :       return v;
     568             : 
     569             :   return RayTracingCommon::invalid_vertex;
     570             : }
     571             : 
     572             : template <typename T>
     573             : bool
     574        8833 : withinEdgeTempl(const Elem * elem,
     575             :                 const Point & point,
     576             :                 ElemExtrema & extrema,
     577             :                 const Real tolerance /* = TRACE_TOLERANCE */)
     578             : {
     579             :   mooseAssert(extrema.isInvalid(), "Should be invalid");
     580             : 
     581       29353 :   for (int e = 0; e < T::num_edges; ++e)
     582       29353 :     if (isWithinSegment(elem->point(T::edge_nodes_map[e][0]),
     583       29353 :                         elem->point(T::edge_nodes_map[e][1]),
     584             :                         point,
     585             :                         tolerance))
     586             :     {
     587        8833 :       extrema.setEdge(T::edge_nodes_map[e][0], T::edge_nodes_map[e][1]);
     588        8833 :       return true;
     589             :     }
     590             : 
     591             :   return false;
     592             : }
     593             : 
     594             : bool
     595        8833 : withinEdge(const Elem * elem,
     596             :            const Point & point,
     597             :            ElemExtrema & extrema,
     598             :            const Real tolerance /* = TRACE_TOLERANCE */)
     599             : {
     600        8833 :   switch (elem->type())
     601             :   {
     602         481 :     case HEX8:
     603             :     case HEX20:
     604             :     case HEX27:
     605         481 :       return withinEdgeTempl<Hex8>(elem, point, extrema, tolerance);
     606        5760 :     case TET4:
     607             :     case TET10:
     608             :     case TET14:
     609        5760 :       return withinEdgeTempl<Tet4>(elem, point, extrema, tolerance);
     610        1872 :     case PYRAMID5:
     611             :     case PYRAMID13:
     612             :     case PYRAMID14:
     613        1872 :       return withinEdgeTempl<Pyramid5>(elem, point, extrema, tolerance);
     614         720 :     case PRISM6:
     615             :     case PRISM15:
     616             :     case PRISM18:
     617         720 :       return withinEdgeTempl<Prism6>(elem, point, extrema, tolerance);
     618           0 :     default:
     619           0 :       mooseError("Element type ",
     620           0 :                  Utility::enum_to_string(elem->type()),
     621             :                  " not supported in TraceRayTools::withinEdge()");
     622             :   }
     623             : 
     624             :   return false;
     625             : }
     626             : 
     627             : unsigned short
     628     2177867 : atVertexOnSide(const Elem * elem, const Point & point, const unsigned short side)
     629             : {
     630     2177867 :   switch (elem->type())
     631             :   {
     632     1030639 :     case HEX8:
     633             :     case HEX20:
     634             :     case HEX27:
     635     1030639 :       return atVertexOnSideTempl<Hex8>(elem, point, side);
     636       76649 :     case QUAD4:
     637             :     case QUAD8:
     638             :     case QUAD9:
     639       76649 :       return atVertexOnSideTempl<Quad4>(elem, point, side);
     640        5739 :     case TRI3:
     641             :     case TRI6:
     642             :     case TRI7:
     643        5739 :       return atVertexOnSideTempl<Tri3>(elem, point, side);
     644      317316 :     case TET4:
     645             :     case TET10:
     646             :     case TET14:
     647      317316 :       return atVertexOnSideTempl<Tet4>(elem, point, side);
     648      220777 :     case PYRAMID5:
     649             :     case PYRAMID13:
     650             :     case PYRAMID14:
     651      220777 :       return atVertexOnSideTempl<Pyramid5>(elem, point, side);
     652      526735 :     case PRISM6:
     653             :     case PRISM15:
     654             :     case PRISM18:
     655      526735 :       return atVertexOnSideTempl<Prism6>(elem, point, side);
     656          12 :     case EDGE2:
     657             :     case EDGE3:
     658             :     case EDGE4:
     659          12 :       return atVertexOnSideTempl<Edge2>(elem, point, side);
     660           0 :     default:
     661           0 :       mooseError("Element type ",
     662           0 :                  Utility::enum_to_string(elem->type()),
     663             :                  " not supported in TraceRayTools::atVertexOnSide()");
     664             :   }
     665             : 
     666             :   return RayTracingCommon::invalid_vertex;
     667             : }
     668             : 
     669             : template <typename T>
     670             : typename std::enable_if<!std::is_base_of<Edge, T>::value, unsigned short>::type
     671     2177855 : atVertexOnSideTempl(const Elem * elem, const Point & point, const unsigned short side)
     672             : {
     673             :   mooseAssert(side < elem->n_sides(), "Invalid side");
     674             :   mooseAssert(elem->side_ptr(side)->close_to_point(point, LOOSE_TRACE_TOLERANCE),
     675             :               "Side does not contain point");
     676             : 
     677     8344092 :   for (int i = 0; i < nodesPerSide<T>(side); ++i)
     678     6465987 :     if (elem->point(T::side_nodes_map[side][i]).absolute_fuzzy_equals(point, TRACE_TOLERANCE))
     679     1221073 :       return T::side_nodes_map[side][i];
     680             : 
     681             :   return RayTracingCommon::invalid_vertex;
     682             : }
     683             : 
     684             : template <typename T>
     685             : typename std::enable_if<std::is_base_of<Edge, T>::value, unsigned short>::type
     686          12 : atVertexOnSideTempl(const Elem * elem, const Point & point, const unsigned short side)
     687             : {
     688             :   mooseAssert(side < elem->n_sides(), "Invalid side");
     689             :   mooseAssert(elem->side_ptr(side)->close_to_point(point, LOOSE_TRACE_TOLERANCE),
     690             :               "Side does not contain point");
     691             : 
     692          12 :   if (elem->point(side).absolute_fuzzy_equals(point, TRACE_TOLERANCE))
     693          12 :     return side;
     694             : 
     695             :   return RayTracingCommon::invalid_vertex;
     696             : }
     697             : 
     698             : bool
     699      977974 : withinEdgeOnSide(const Elem * const elem,
     700             :                  const Point & point,
     701             :                  const unsigned short side,
     702             :                  ElemExtrema & extrema)
     703             : {
     704      977974 :   switch (elem->type())
     705             :   {
     706      398934 :     case HEX8:
     707             :     case HEX20:
     708             :     case HEX27:
     709      398934 :       return withinEdgeOnSideTempl<Hex8>(elem, point, side, extrema);
     710      154580 :     case TET4:
     711             :     case TET10:
     712             :     case TET14:
     713      154580 :       return withinEdgeOnSideTempl<Tet4>(elem, point, side, extrema);
     714      150760 :     case PYRAMID5:
     715             :     case PYRAMID13:
     716             :     case PYRAMID14:
     717      150760 :       return withinEdgeOnSideTempl<Pyramid5>(elem, point, side, extrema);
     718      273700 :     case PRISM6:
     719             :     case PRISM15:
     720             :     case PRISM18:
     721      273700 :       return withinEdgeOnSideTempl<Prism6>(elem, point, side, extrema);
     722           0 :     default:
     723           0 :       mooseError("Element type ",
     724           0 :                  Utility::enum_to_string(elem->type()),
     725             :                  " not supported in TraceRayTools::withinEdgeOnSide()");
     726             :   }
     727             : 
     728             :   return false;
     729             : }
     730             : 
     731             : template <typename T>
     732             : typename std::enable_if<std::is_base_of<Cell, T>::value, bool>::type
     733      977974 : withinEdgeOnSideTempl(const Elem * const elem,
     734             :                       const Point & point,
     735             :                       const unsigned short side,
     736             :                       ElemExtrema & extrema)
     737             : {
     738             :   mooseAssert(side < elem->n_sides(), "Invalid side");
     739             :   mooseAssert(elem->side_ptr(side)->close_to_point(point, LOOSE_TRACE_TOLERANCE),
     740             :               "Side does not contain point");
     741             :   mooseAssert(extrema.isInvalid(), "Should be invalid");
     742             : 
     743      977974 :   int last_n = T::side_nodes_map[side][nodesPerSide<T>(side) - 1];
     744             : 
     745     2300102 :   for (int side_v = 0; side_v < nodesPerSide<T>(side); ++side_v)
     746     2296694 :     if (isWithinSegment(elem->point(last_n), elem->point(T::side_nodes_map[side][side_v]), point))
     747             :     {
     748      974566 :       extrema.setEdge(last_n, T::side_nodes_map[side][side_v]);
     749             :       mooseAssert(extrema.buildEdge(elem)->close_to_point(point, LOOSE_TRACE_TOLERANCE),
     750             :                   "Edge doesn't contain point");
     751      974566 :       return true;
     752             :     }
     753             :     else
     754     1322128 :       last_n = T::side_nodes_map[side][side_v];
     755             : 
     756             :   return false;
     757             : }
     758             : 
     759             : bool
     760     2148372 : withinExtremaOnSide(const Elem * const elem,
     761             :                     const Point & point,
     762             :                     const unsigned short side,
     763             :                     const unsigned int dim,
     764             :                     ElemExtrema & extrema)
     765             : {
     766             :   mooseAssert(extrema.isInvalid(), "Extrema should be invalid");
     767             :   mooseAssert(dim == elem->dim(), "Incorrect dim");
     768             : 
     769     2148372 :   extrema.first = atVertexOnSide(elem, point, side);
     770             :   if (extrema.atVertex())
     771             :     return true;
     772      953254 :   if (dim == 3 && withinEdgeOnSide(elem, point, side, extrema))
     773             :     return true;
     774             : 
     775             :   return false;
     776             : }
     777             : 
     778             : bool
     779     2429038 : isWithinSegment(const Point & segment1,
     780             :                 const Point & segment2,
     781             :                 const Point & point,
     782             :                 const Real tolerance /* = TRACE_TOLERANCE */)
     783             : {
     784             :   mooseAssert(!segment1.absolute_fuzzy_equals(segment2, TRACE_TOLERANCE), "Same endpoints");
     785             : 
     786     2429038 :   const auto segment_length = (segment1 - segment2).norm();
     787     2429038 :   return isWithinSegment(segment1, segment2, segment_length, point, tolerance);
     788             : }
     789             : 
     790             : bool
     791    12050111 : isWithinSegment(const Point & segment1,
     792             :                 const Point & segment2,
     793             :                 const Real segment_length,
     794             :                 const Point & point,
     795             :                 const Real tolerance /* = TRACE_TOLERANCE */)
     796             : {
     797             :   mooseAssert(!segment1.absolute_fuzzy_equals(segment2, TRACE_TOLERANCE), "Same endpoints");
     798             :   mooseAssert(MooseUtils::absoluteFuzzyEqual((segment1 - segment2).norm(), segment_length),
     799             :               "Invalid segment length");
     800             : 
     801             :   const auto diff1 = point - segment1;
     802             :   const auto diff2 = point - segment2;
     803             : 
     804    12050111 :   if (diff1 * diff2 > tolerance * segment_length)
     805             :     return false;
     806             : 
     807     1443029 :   return std::abs(diff1.norm() + diff2.norm() - segment_length) < tolerance * segment_length;
     808             : }
     809             : 
     810             : bool
     811     4116843 : onBoundingBoxBoundary(const BoundingBox & bbox,
     812             :                       const Point & point,
     813             :                       const unsigned int dim,
     814             :                       const Real tolerance)
     815             : {
     816    14039555 :   for (unsigned int d = 0; d < dim; ++d)
     817    11039902 :     if (MooseUtils::absoluteFuzzyEqual(point(d), bbox.min()(d), tolerance) ||
     818             :         MooseUtils::absoluteFuzzyEqual(point(d), bbox.max()(d), tolerance))
     819             :       return true;
     820             : 
     821             :   return false;
     822             : }
     823             : }

Generated by: LCOV version 1.14