https://mooseframework.inl.gov
TraceRay.C
Go to the documentation of this file.
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"
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 
48  : _study(study),
49  _mesh(study.getSubProblem().mesh()),
50  _dim(_mesh.dimension()),
51  _boundary_info(_mesh.getMesh().get_boundary_info()),
52  _pid(_study.comm().rank()),
53  _tid(tid),
54  _backface_culling(false),
55  _current_normals(nullptr),
56  _results(ENDED_STATIONARY + 1)
57 {
58 }
59 
60 void
62 {
63  _current_subdomain_id = Elem::invalid_subdomain_id;
65 
66  // Zero out all results
67  for (auto & val : _results)
68  val = 0;
69 
72 }
73 
74 void
76 {
77  // Invalidate the vertex and edge neighbor caches
78  _vertex_neighbors.clear();
79  _edge_neighbors.clear();
80 }
81 
83 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  switch (elem_type)
107  {
108  case HEX8:
109  intersected = exitsElem<Hex8, Hex8>(elem,
110  incoming_side,
111  intersection_point,
112  intersected_side,
113  intersected_extrema,
114  intersection_distance,
115  normals);
116  break;
117  case TET4:
118  intersected = exitsElem<Tet4, Tet4>(elem,
119  incoming_side,
120  intersection_point,
121  intersected_side,
122  intersected_extrema,
123  intersection_distance,
124  normals);
125  break;
126  case PYRAMID5:
127  intersected = exitsElem<Pyramid5, Pyramid5>(elem,
128  incoming_side,
129  intersection_point,
130  intersected_side,
131  intersected_extrema,
132  intersection_distance,
133  normals);
134  break;
135  case PRISM6:
136  intersected = exitsElem<Prism6, Prism6>(elem,
137  incoming_side,
138  intersection_point,
139  intersected_side,
140  intersected_extrema,
141  intersection_distance,
142  normals);
143  break;
144  case QUAD4:
145  intersected = exitsElem<Quad4, Quad4>(elem,
146  incoming_side,
147  intersection_point,
148  intersected_side,
149  intersected_extrema,
150  intersection_distance,
151  normals);
152  break;
153  case TRI3:
154  intersected = exitsElem<Tri3, Tri3>(elem,
155  incoming_side,
156  intersection_point,
157  intersected_side,
158  intersected_extrema,
159  intersection_distance,
160  normals);
161  break;
162  case HEX20:
163  intersected = exitsElem<Hex20, Hex8>(elem,
164  incoming_side,
165  intersection_point,
166  intersected_side,
167  intersected_extrema,
168  intersection_distance,
169  normals);
170  break;
171  case HEX27:
172  intersected = exitsElem<Hex27, Hex8>(elem,
173  incoming_side,
174  intersection_point,
175  intersected_side,
176  intersected_extrema,
177  intersection_distance,
178  normals);
179  break;
180  case QUAD8:
181  intersected = exitsElem<Quad8, Quad4>(elem,
182  incoming_side,
183  intersection_point,
184  intersected_side,
185  intersected_extrema,
186  intersection_distance,
187  normals);
188  break;
189  case QUAD9:
190  intersected = exitsElem<Quad9, Quad4>(elem,
191  incoming_side,
192  intersection_point,
193  intersected_side,
194  intersected_extrema,
195  intersection_distance,
196  normals);
197  break;
198  case TRI6:
199  intersected = exitsElem<Tri6, Tri3>(elem,
200  incoming_side,
201  intersection_point,
202  intersected_side,
203  intersected_extrema,
204  intersection_distance,
205  normals);
206  break;
207  case TRI7:
208  intersected = exitsElem<Tri7, Tri3>(elem,
209  incoming_side,
210  intersection_point,
211  intersected_side,
212  intersected_extrema,
213  intersection_distance,
214  normals);
215  break;
216  case TET10:
217  intersected = exitsElem<Tet10, Tet4>(elem,
218  incoming_side,
219  intersection_point,
220  intersected_side,
221  intersected_extrema,
222  intersection_distance,
223  normals);
224  break;
225  case TET14:
226  intersected = exitsElem<Tet14, Tet4>(elem,
227  incoming_side,
228  intersection_point,
229  intersected_side,
230  intersected_extrema,
231  intersection_distance,
232  normals);
233  break;
234  case PYRAMID13:
235  intersected = exitsElem<Pyramid13, Pyramid5>(elem,
236  incoming_side,
237  intersection_point,
238  intersected_side,
239  intersected_extrema,
240  intersection_distance,
241  normals);
242  break;
243  case PYRAMID14:
244  intersected = exitsElem<Pyramid14, Pyramid5>(elem,
245  incoming_side,
246  intersection_point,
247  intersected_side,
248  intersected_extrema,
249  intersection_distance,
250  normals);
251  break;
252  case PRISM15:
253  intersected = exitsElem<Prism15, Prism6>(elem,
254  incoming_side,
255  intersection_point,
256  intersected_side,
257  intersected_extrema,
258  intersection_distance,
259  normals);
260  break;
261  case PRISM18:
262  intersected = exitsElem<Prism18, Prism6>(elem,
263  incoming_side,
264  intersection_point,
265  intersected_side,
266  intersected_extrema,
267  intersection_distance,
268  normals);
269  break;
270  case EDGE2:
271  intersected = exitsElem<Edge2, Edge2>(elem,
272  incoming_side,
273  intersection_point,
274  intersected_side,
275  intersected_extrema,
276  intersection_distance,
277  normals);
278  break;
279  case EDGE3:
280  intersected = exitsElem<Edge3, Edge2>(elem,
281  incoming_side,
282  intersection_point,
283  intersected_side,
284  intersected_extrema,
285  intersection_distance,
286  normals);
287  break;
288  case EDGE4:
289  intersected = exitsElem<Edge4, Edge2>(elem,
290  incoming_side,
291  intersection_point,
292  intersected_side,
293  intersected_extrema,
294  intersection_distance,
295  normals);
296  break;
297  default:
298  mooseError(
299  "Element type ", Utility::enum_to_string(elem->type()), " not supported by TraceRay");
300  }
301 
302  if (intersected)
303  {
304  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
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 {
323 
324  debugRay("Called exitsElem() in 2D or 3D");
325 
326  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  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  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  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  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  if (s == incoming_side)
369  {
370  debugRay(" Skipping due to incoming side");
371  if (++s == T::num_sides)
372  break;
373  else
374  continue;
375  }
376 
377  // Using backface culling on this run through the sides
378  // If the direction is non-entrant, skip this side
379  if (use_backface_culling)
380  {
381  // Side is non-entrant per the culling, so skip
382  if (normals[s] * direction < -LOOSE_TRACE_TOLERANCE)
383  {
384  debugRay(" Skipping due to backface culling dot = ", normals[s] * direction);
385 
386  if (++s == T::num_sides)
387  break;
388  else
389  continue;
390 
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  else if (_backface_culling)
399  {
400  // Side was non-entrant per the culling, so try again
401  if (normals[s] * direction >= -LOOSE_TRACE_TOLERANCE)
402  {
403  debugRay(" Skipping because we already checked this side with culling enabled");
404  if (++s == T::num_sides)
405  break;
406  else
407  continue;
408  }
409  else
410  debugRay(" Side that was skipped due to culling");
411 
413  }
414  }
415 
416  // Look for an intersection!
417  current_intersection_point = RayTracingCommon::invalid_point;
418  current_intersected_extrema.invalidate();
419  const bool intersected = sideIntersectedByLine<FirstOrderT>(elem,
421  direction,
422  s,
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  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
447  !_elem_side_builder(*elem, s).contains_point(current_intersection_point))
448  failTrace("Intersected side does not contain intersection point",
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  if (current_intersection_distance > best_intersection_distance)
456  {
457  debugRay(" Best intersection so far");
458 
459  intersected_side = s;
460  intersection_distance = current_intersection_distance;
461  intersection_point = current_intersection_point;
462  intersected_extrema = current_intersected_extrema;
463  best_intersection_distance = current_intersection_distance;
464  }
465  }
466 
467  if (++s == T::num_sides || try_nonplanar_incoming_side)
468  break;
469  else
470  continue;
471  } // while(true)
472 
473  // Found an intersection
474  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  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  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  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
502  _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  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 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 {
526 
527  debugRay("Called exitsElem() in 1D");
528 
529  // Scale the tolerance based on the element size
530  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  if (incoming_side != RayTracingCommon::invalid_side)
535  {
536  intersected_side = (incoming_side == 1 ? 0 : 1);
537  intersected_extrema.setVertex(intersected_side);
538  intersection_point = elem->point(intersected_side);
539  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  return true;
545  }
546 
547  // End point that is for sure out of the element
548  const Point extended_end_point =
549  _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  for (MooseIndex(elem->n_sides()) side = 0; side < elem->n_sides(); ++side)
554  {
555  const Point side_point = elem->point(side);
556  debugRay(" Checking side ", side, " at ", side_point);
557 
558  const Real incoming_to_side = (side_point - _incoming_point).norm();
559  if (incoming_to_side < tol)
560  {
561  debugRay(" Continuing because at side");
562  continue;
563  }
564 
565  const Real incoming_to_end = (extended_end_point - _incoming_point).norm();
566  const Real side_to_end = (extended_end_point - side_point).norm();
567  const Real sum = incoming_to_side + side_to_end - incoming_to_end;
568  debugRay(" Sum = ", sum);
569 
570  if (std::abs(sum) < tol)
571  {
572  intersected_side = side;
573  intersected_extrema.setVertex(side);
574  intersection_point = side_point;
575  intersection_distance = incoming_to_side;
576  debugRay(" Intersected at ", intersection_point);
577  return true;
578  }
579  }
580 
581  return false;
582 }
583 
585 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 {
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");
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  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  if (neighbor_info._elem == last_elem)
625  {
626  debugRay("Skipping last elem ", last_elem->id());
627  last_elem_info = &neighbor_info;
628  continue;
629  }
630 
631  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  if (exit_result != NO_EXIT)
640  {
641  debugRay("Ray can exit through neighbor ", neighbor_info._elem->id());
642 
643  if (current_intersection_distance > longest_distance)
644  {
645  best_elem = neighbor_info._elem;
646  best_elem_incoming_side = current_incoming_side;
647  _intersection_point = current_intersection_point;
648  _intersected_side = current_intersected_side;
649  _intersected_extrema = current_intersected_extrema;
650  _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  if (!best_elem && last_elem_info)
659  {
660  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  if (exit_result != NO_EXIT && current_intersection_distance > longest_distance)
668  {
669  debugRay("Ray can exit through last_elem ", last_elem->id());
670 
671  best_elem = last_elem;
672  best_elem_incoming_side = current_incoming_side;
673  _intersection_point = current_intersection_point;
674  _intersected_side = current_intersected_side;
675  _intersected_extrema = current_intersected_extrema;
676  _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  return best_exit_result;
695 }
696 
699  unsigned short & incoming_side,
700  Point & intersection_point,
701  unsigned short & intersected_side,
702  ElemExtrema & intersected_extrema,
703  Real & intersection_distance)
704 {
705  if (!neighbor_info._valid)
706  return NO_EXIT;
707 
708  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  incoming_side = RayTracingCommon::invalid_side;
713  for (MooseIndex(neighbor_info._sides.size()) i = 0; i < neighbor_info._sides.size(); ++i)
714  if (neighbor_info._side_normals[i] * (*_current_ray)->direction() < LOOSE_TRACE_TOLERANCE)
715  {
716  incoming_side = neighbor_info._sides[i];
717  break;
718  }
719 
720  // No entrant sides on this neighbor were found
721  if (incoming_side == RayTracingCommon::invalid_side)
722  return NO_EXIT;
723 
724  intersection_point = RayTracingCommon::invalid_point;
725  intersected_side = RayTracingCommon::invalid_side;
726  intersected_extrema.invalidate();
727  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  exitsElem(neighbor,
733  neighbor->type(),
734  incoming_side,
735  intersection_point,
736  intersected_side,
737  intersected_extrema,
738  intersection_distance,
739  _backface_culling ? _study.getElemNormals(neighbor, _tid) : nullptr);
740  debugRay("Done with exitsElem() from moveThroughNeighbor()");
741 
742  return exit_result;
743 }
744 
745 void
746 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  _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  if (_dim != 1 && _intersected_extrema.atExtrema())
760  {
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  for (const auto & neighbor_info : neighbors)
770  {
771  if (!neighbor_info._valid)
772  continue;
773 
774  const Elem * elem = neighbor_info._elem;
775  const auto & sides = neighbor_info._sides;
776  const auto & side_normals = neighbor_info._side_normals;
777 
778  for (MooseIndex(side_normals.size()) i = 0; i < side_normals.size(); ++i)
779  if (!elem->neighbor_ptr(sides[i]) // is a boundary side that has our point
780  && side_normals[i] * ray->direction() > TRACE_TOLERANCE) // and is entrant
781  {
782  // TODO: this could likely be optimized
783  ElemExtrema extrema;
784  withinExtremaOnSide(elem, _intersection_point, sides[i], _dim, extrema);
785 
787  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  {
798  }
799 
800  debugRay("Calling external onBoundary() with ", _boundary_elems.size(), " boundaries");
801  onBoundary(ray, /* external = */ true);
802 }
803 
804 void
805 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");
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  _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  if (_dim != 1 && _intersected_extrema.atExtrema())
828  {
829  debugRay("Checking point neighbors for internal sidesets");
830 
831  // Get the neighbors
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  for (const auto & neighbor_info : neighbors)
841  {
842  if (!neighbor_info._valid)
843  continue;
844 
845  const Elem * elem = neighbor_info._elem;
846 
847  // Grab the internal sidesets for this elem
848  const auto & sidesets = _study.getInternalSidesets(elem);
849  // It has none to contribute, so we can continue
850  if (sidesets.empty())
851  {
852  debugRay(" Elem ", elem->id(), " has no internal sidesets");
853  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  for (std::size_t i = 0; i < sides.size(); ++i)
862  {
863  const auto side = sides[i];
864  // Side has internal sidesets and is entrant
865  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  withinExtremaOnSide(elem, _intersection_point, side, _dim, temp_extrema);
870 
871  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  const auto & current_elem_sidesets = _study.getInternalSidesets(_current_elem);
881  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 :-)
889  mooseError("Internal sidesets are not currently supported with adaptivity in tracing");
890 
891  // Special case for 1D
892  if (_dim == 1)
894 
897  current_elem_sidesets[_incoming_side],
898  _dim != 1 ? _intersected_extrema : temp_extrema);
899  }
900 
901  const auto & last_elem_sidesets = _study.getInternalSidesets(_last_elem);
902  if (last_elem_sidesets.size() && last_elem_sidesets[_intersected_side].size())
905  last_elem_sidesets[_intersected_side],
907  }
908 
909  if (!_boundary_elems.empty())
910  {
911  debugRay(" Calling internal onBoundary() with ", _boundary_elems.size(), " boundaries");
912  onBoundary(ray, /* external = */ false);
913  }
914 }
915 
916 void
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  for (const auto bnd_id : bnd_ids)
926  {
927  bool found = false;
928  for (const auto & bnd_elem : _boundary_elems)
929  if (bnd_elem.bnd_id == bnd_id)
930  {
931  found = true;
932  break;
933  }
934 
935  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  _boundary_elems.emplace_back(elem, side, bnd_id, extrema);
945  }
946  }
947 }
948 
949 void
950 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  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  for (unsigned short s = 0; s < _current_elem_n_sides; ++s)
971  if (!_current_elem->neighbor_ptr(s) && s != _intersected_side &&
973  (!at_edge || _current_elem->is_node_on_side(_intersected_extrema.second, s)) &&
974  !_study.sideIsIncoming(_current_elem, s, direction, _tid))
975  {
976  debugRay(" Side ", s, " is a boundary side and the Ray exits");
977  boundary_side = s;
978  boundary_extrema = _intersected_extrema;
979  boundary_elem = _current_elem;
980  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
986  debugRay("Checking current element failed, now checking neighbors");
987 
988  debugRay("Found ", neighbors.size(), " candidate neighbors (including self)");
989  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  if (!neighbor_info._valid)
994  continue;
995 
996  const Elem * neighbor = neighbor_info._elem;
997  // We've already checked ourself
998  if (neighbor == _current_elem)
999  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  for (MooseIndex(neighbor_info._sides.size()) i = 0; i < neighbor_info._sides.size(); ++i)
1005  if (neighbor_info._side_normals[i] * direction > TRACE_TOLERANCE &&
1006  !neighbor->neighbor_ptr(neighbor_info._sides[i]))
1007  {
1009  neighbor, _intersection_point, neighbor_info._sides[i], _dim, boundary_extrema);
1010  traceAssert(boundary_extrema.atExtrema(), "Should be at extrema");
1011  boundary_side = neighbor_info._sides[i];
1012  boundary_elem = neighbor;
1013  return;
1014  }
1015  }
1016 }
1017 
1018 void
1019 TraceRay::trace(const std::shared_ptr<Ray> & ray)
1020 {
1021  mooseAssert(_study.currentlyPropagating(), "Should only use while propagating rays");
1022 
1023  _current_ray = &ray;
1024  _current_elem = ray->currentElem();
1025  _last_elem = nullptr;
1026  _incoming_point = ray->currentPoint();
1027  _incoming_side = ray->currentIncomingSide();
1028  _should_continue = true;
1029 
1030  _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  if (_study.verifyRays() && !ray->invalidCurrentIncomingSide() && ray->maxDistance() > 0 &&
1038  !_study.sideIsIncoming(_current_elem, _incoming_side, ray->direction(), _tid))
1039  failTrace("Ray incoming side is not incoming", /* warning = */ false, __LINE__);
1040 
1041 #ifdef DEBUG_RAY_IF
1042  if (DEBUG_RAY_IF)
1043  libMesh::err << "\n\n";
1044 #endif
1045  debugRay("At top of trace for Ray");
1046  debugRay("Top of trace loop Ray info\n", ray->getInfo());
1047  debugRay("Top of trace loop starting elem info\n", ray->currentElem()->get_info());
1048 
1049  // Invalidate this up front because it's copied immedtiately into _last_intersected_extrema
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);
1059  }
1060 #endif
1061 
1062  // Caching trace along the way: init for this Ray
1063  if (_study.shouldCacheTrace(ray))
1064  {
1065  debugRay("Trying to init threaded cached trace");
1066 
1068 
1069  // Add starting data
1070  if (_study.dataOnCacheTraces())
1071  _current_cached_trace->lastPoint()._data = ray->data();
1073  _current_cached_trace->lastPoint()._aux_data = ray->auxData();
1074  }
1075  else
1076  _current_cached_trace = nullptr;
1077 
1078  // Need to call subdomain setup
1080  onSubdomainChanged(ray, /* same_ray = */ false);
1081  // If we didn't change subdomains, we still need to call this on the RayKernels
1082  else
1084  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
1108  // Invalidate all intersections as we're tracing again
1109  _exits_elem = false;
1114 
1115  // Stationary ray
1116  if (ray->stationary())
1117  {
1118  mooseAssert(ray->invalidDirection(), "Should have an invalid direction");
1119  _exits_elem = true;
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
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  if (_backface_culling)
1138 
1139  const auto exits_elem_result = exitsElem(_current_elem,
1147 
1148  if (exits_elem_result != NO_EXIT)
1149  {
1150  storeExitsElemResult(exits_elem_result);
1151  _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  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
1178  {
1179  traceAssert(_last_elem, "Should be valid");
1180  move_through_neighbors_last = _last_elem;
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 ",
1191  " on elem ",
1192  _current_elem->id(),
1193  " at ",
1194  _incoming_point);
1195 
1196  move_through_neighbors_last = _current_elem;
1197 
1198  // If we have side info: check for vertices on said side, otherwise, check everywhere
1199  const auto at_v = _incoming_side != RayTracingCommon::invalid_side
1202 
1204  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  else if (_dim == 3)
1208  {
1209  ElemExtrema extrema;
1212  else
1214 
1215  if (extrema.atEdge())
1216  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  if (!neighbors || neighbors->empty())
1222 
1223  // Couldn't find anything
1224  if (neighbors->empty())
1225  {
1226  failTrace("Could not find neighbors to move through", _study.tolerateFailure(), __LINE__);
1227  return;
1228  }
1229  }
1230 
1231  // Move through a neighbor
1232  const Elem * best_neighbor = nullptr;
1233  auto best_neighbor_side = RayTracingCommon::invalid_side;
1234  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  if (exits_elem_result == NO_EXIT)
1239  {
1240  failTrace("Could not find intersection after trying to move through point neighbors",
1242  __LINE__);
1243  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  _exits_elem = true;
1250  _current_elem = best_neighbor;
1251  _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  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);
1264 
1266  return;
1267  }
1268 
1269  // Subdomain changed
1271  onSubdomainChanged(ray, /* same_ray = */ true);
1272 
1273  // Do own this element - tally the result as we're the ones tracing it
1274  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: ",
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  if (_intersection_distance > 0)
1290  {
1291  debugRay("Incrementing ray intersections by 1 to ", ray->intersections() + 1);
1292  ray->addIntersection();
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  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  if (MooseUtils::absoluteFuzzyEqual(ray->distance(), max_distance))
1311  {
1312  debugRay("At max distance");
1313 
1314  ray->setShouldContinue(false);
1315  _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  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  _intersection_point -= ray->direction() * difference;
1332  _intersection_distance -= difference;
1335  ray->setCurrentPoint(_intersection_point);
1336 
1337  ray->setShouldContinue(false);
1338  _should_continue = false;
1339 
1340  traceAssert(_intersection_distance >= 0, "Negative _intersection_distance");
1341 #ifndef NDEBUG
1343  failTrace("Does not contain point after past max distance",
1344  /* warning = */ false,
1345  __LINE__);
1346 #endif
1347  }
1348 
1349  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  onSegment(ray);
1360 
1361  _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  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  onCompleteTrace(ray);
1372  return;
1373  }
1374 
1375  // RayKernel moved a Ray
1376  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  _incoming_point = ray->currentPoint();
1387  ray->setCurrentIncomingSide(_incoming_side);
1388 
1389  const auto new_intersection_distance = (ray->currentPoint() - _incoming_point).norm();
1390  ray->addDistance(-_intersection_distance + new_intersection_distance);
1391  _intersection_distance = new_intersection_distance;
1392 
1393  onTrajectoryChanged(ray);
1394  onContinueTrace(ray);
1395  continue;
1396  }
1397  else
1398  possiblyAddDebugRayMeshPoint(_incoming_point, _intersection_point);
1399  }
1400  else
1401  {
1402  _study.postOnSegment(_tid, ray);
1403 
1404  if (!_should_continue)
1405  {
1406  debugRay("Killing due to at end without RayKernels");
1407  traceAssert(!ray->shouldContinue(), "Ray shouldn't continue");
1408 
1409  onCompleteTrace(ray);
1410  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  if (_dim > 1 && _intersected_extrema.atExtrema() &&
1424  _dim,
1426  {
1427  auto boundary_side = RayTracingCommon::invalid_side;
1428  ElemExtrema boundary_extrema;
1429  const Elem * boundary_elem = nullptr;
1430 
1431  findExternalBoundarySide(boundary_side, boundary_extrema, boundary_elem);
1432 
1433  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.
1440  _current_elem = boundary_elem;
1441  _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
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  neighbor = _study.hasSameLevelActiveElems()
1468 
1469  // Found one - not on the boundary so set the next info
1470  if (neighbor)
1471  {
1472  traceAssert(neighbor->active(), "Inactive neighbor");
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
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  const unsigned short n_sides = neighbor->subdomain_id() == _current_subdomain_id
1485  : neighbor->n_sides();
1486  traceAssert(n_sides == neighbor->n_sides(), "n_sides incorrect");
1487 
1488  for (_incoming_side = 0; _incoming_side < n_sides; ++_incoming_side)
1489  if (neighbor->neighbor_ptr(_incoming_side) == _current_elem)
1490  break;
1491  }
1492  else
1493  _incoming_side = neighbor->which_neighbor_am_i(_current_elem);
1494 
1496  _current_elem = neighbor;
1497  ray->setCurrentElem(neighbor);
1498  ray->setCurrentIncomingSide(_incoming_side);
1499 
1500  debugRay("Next elem: ", neighbor->id(), " with centroid ", neighbor->vertex_average());
1501  debugRay("Next _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  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  if (_study.hasInternalSidesets() && (subdomain_changed || _intersected_extrema.atExtrema()))
1516  {
1518 
1519  // Internal RayBC killed a Ray
1520  if (!_should_continue)
1521  {
1522  traceAssert(!ray->shouldContinue(), "Should be the same");
1523  debugRay("Internal RayBC killed the ray");
1524 
1525  onCompleteTrace(ray);
1526  return;
1527  }
1528 
1529  // Internal RayBC changed the Ray
1530  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  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  if (dot > -TRACE_TOLERANCE)
1540  {
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  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  if (neighbor->processor_id() != _pid)
1563  {
1565  {
1566  debugRay("Neighbor is off processor but continuing to move through neighbors");
1567  }
1568  else
1569  {
1571  return;
1572  }
1573  }
1574 
1575  // Neighbor is on processor, call subdomain setup if needed
1576  if (subdomain_changed)
1577  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
1586 
1587  traceAssert(ray->currentPoint().absolute_fuzzy_equals(_intersection_point, TRACE_TOLERANCE),
1588  "RayBC changed the Ray point");
1589 
1590  // Quit tracing if the Ray was killed by a BC
1591  if (!_should_continue)
1592  {
1593  traceAssert(!ray->shouldContinue(), "Should be the same");
1594  debugRay("Exiting due to death by BC");
1595 
1596  onCompleteTrace(ray);
1597  return;
1598  }
1599  // RayBC changed the direction of the Ray
1600  if (ray->trajectoryChanged())
1601  {
1602  debugRay("RayBC changed the trajectory");
1603  debugRay(" new direction = ", ray->direction());
1604  traceAssert(ray->direction() *
1607  "Reflected ray is not incoming");
1608  possiblyAddDebugRayMeshPoint(_incoming_point, _intersection_point);
1609 
1613  ray->setCurrentPoint(_incoming_point);
1614  ray->setCurrentIncomingSide(_incoming_side);
1615  onTrajectoryChanged(ray);
1616  }
1617  }
1618 
1619  onContinueTrace(ray);
1620  } while (true);
1621 
1622  // If a trace made its way down here and didn't return... it failed
1623  failTrace("Could not find an intersection", _study.tolerateFailure(), __LINE__);
1624 }
1625 
1626 void
1627 TraceRay::onCompleteTrace(const std::shared_ptr<Ray> & ray)
1628 {
1630  rk->postTrace();
1631 
1632  debugRay("Called onCompleteTrace()\n", (*_current_ray)->getInfo());
1633  if (_intersection_distance > 0)
1634  possiblyAddDebugRayMeshPoint(_incoming_point, _intersection_point);
1635  possiblySaveDebugRayMesh();
1636 
1638  {
1639  _current_cached_trace->_last = true;
1640 
1641  if (_intersection_distance > 0)
1642  {
1643  _current_cached_trace->addPoint(ray->currentPoint());
1644  if (_study.dataOnCacheTraces())
1645  _current_cached_trace->lastPoint()._data = ray->data();
1647  _current_cached_trace->lastPoint()._aux_data = ray->auxData();
1648  }
1649 
1650  mooseAssert(ray->stationary() == _current_cached_trace->stationary(), "Stationary mismatch");
1651  }
1652 }
1653 
1654 void
1655 TraceRay::onContinueTrace(const std::shared_ptr<Ray> & ray)
1656 {
1657  traceAssert(ray->shouldContinue(), "Ray must continue");
1658 
1660  {
1661  _current_cached_trace->addPoint(ray->currentPoint());
1662  if (_study.dataOnCacheTraces())
1663  _current_cached_trace->lastPoint()._data = ray->data();
1665  _current_cached_trace->lastPoint()._aux_data = ray->auxData();
1666  }
1667 }
1668 
1669 void
1670 TraceRay::continueTraceOffProcessor(const std::shared_ptr<Ray> & ray)
1671 {
1672  traceAssert(ray->currentElem() == _current_elem, "Ray currentElem() invalid");
1673  traceAssert(ray->currentIncomingSide() == _incoming_side, "Ray currentIncomingSide() invalid");
1674  traceAssert(ray->currentPoint() == _incoming_point, "Ray currentPoint() invalid");
1675  traceAssert(_current_elem->processor_id() != _pid, "Off processor trace is not off processor");
1676  debugRay("Ray going off processor to ", _current_elem->processor_id());
1677 
1678  ray->addProcessorCrossing();
1679 
1681  {
1683  if (_study.dataOnCacheTraces())
1684  _current_cached_trace->lastPoint()._data = ray->data();
1686  _current_cached_trace->lastPoint()._aux_data = ray->auxData();
1687  }
1688 
1689  if (_intersection_distance > 0)
1690  possiblyAddDebugRayMeshPoint(_incoming_point, _intersection_point);
1691  possiblySaveDebugRayMesh();
1692 }
1693 
1694 void
1695 TraceRay::onTrajectoryChanged(const std::shared_ptr<Ray> & ray)
1696 {
1697 #ifndef NDEBUG
1700  ? !_current_elem->close_to_point(ray->currentPoint(), LOOSE_TRACE_TOLERANCE)
1701  : !_current_elem->contains_point(ray->currentPoint())))
1702  failTrace("Elem does not contain point after trajectory change",
1703  /* warning = */ false,
1704  __LINE__);
1705 #endif
1706 
1707  traceAssert(ray->shouldContinue(), "Ray should continue when trajectory is being changed");
1708 
1709  ray->setTrajectoryChanged(false);
1710  ray->addTrajectoryChange();
1711 
1713  {
1714  if (_intersection_distance > 0)
1715  _current_cached_trace->addPoint(ray->currentPoint());
1716  if (_study.dataOnCacheTraces())
1717  _current_cached_trace->lastPoint()._data = ray->data();
1719  _current_cached_trace->lastPoint()._aux_data = ray->auxData();
1720  }
1721 }
1722 
1723 void
1724 TraceRay::onSubdomainChanged(const std::shared_ptr<Ray> & ray, const bool same_ray)
1725 {
1726  debugRay("Calling onSubdomainChanged() on subdomain ", _current_elem->subdomain_id());
1727  debugRay(" _current_subdomain_id = ", _current_subdomain_id);
1728 
1733 
1734  if (_has_ray_kernels)
1735  {
1736  auto & current_ray_kernels = _study.currentRayKernels(_tid);
1737 
1738  // If we're still tracing the same Ray, keep track of our old RayKernels
1739  // so that we don't call preTrace() on them again
1740  if (same_ray)
1741  _old_ray_kernels.insert(current_ray_kernels.begin(), current_ray_kernels.end());
1742  // If we're not tracing the same Ray, we need to call preTrace() on everything
1743  else
1744  _old_ray_kernels.clear();
1745 
1746  // Call segmentSubdomainSetup to get new kernels etc
1748 
1749  // Call preTrace() on all of the RayKernels that need it
1750  for (RayKernelBase * rk : current_ray_kernels)
1751  // Haven't called preTrace() for this Ray on this RayKernel yet
1752  if (!_old_ray_kernels.count(rk))
1753  rk->preTrace();
1754  }
1755 }
1756 
1757 std::string
1758 TraceRay::failTraceMessage(const std::string & reason, const int line)
1759 {
1760  std::stringstream oss;
1761  oss << "Ray on processor " << _pid << " and thread " << _tid << " failed to trace";
1762  if (line != -1)
1763  oss << " at line " << line;
1764  oss << "\n\n" << reason << "\n\n";
1765  oss << ((*_current_ray))->getInfo() << "\n";
1766  oss << "Current trace information\n";
1767  oss << " _current_subdomain_id = ";
1768  if (_current_subdomain_id == Elem::invalid_subdomain_id)
1769  oss << "invalid subdomain id\n";
1770  else
1771  oss << _current_subdomain_id << "\n";
1772  oss << " _current_elem_type = " << Utility::enum_to_string(_current_elem_type) << "\n";
1773  oss << " _current_elem_n_sides = " << _current_elem_n_sides << "\n";
1774  oss << " _incoming_point = ";
1776  oss << "invalid point\n";
1777  else
1778  oss << _incoming_point << "\n";
1779  oss << " _incoming_side = ";
1781  oss << "invalid side\n";
1782  else
1783  oss << _incoming_side << "\n";
1784  oss << " _intersection_point = ";
1786  oss << "invalid point\n";
1787  else
1788  oss << _intersection_point << "\n";
1789  oss << " _intersected_side = ";
1791  oss << "invalid side\n";
1792  else
1793  oss << _intersected_side << "\n";
1794  oss << " _intersected_extrema = " << _intersected_extrema << "\n";
1795  oss << " _exits_elem = " << _exits_elem << "\n";
1796  if (_current_elem)
1797  oss << _current_elem->get_info();
1798  else
1799  oss << "_current_elem = invalid\n";
1800 
1801  possiblySaveDebugRayMesh();
1802  return oss.str();
1803 }
1804 
1805 void
1806 TraceRay::failTrace(const std::string & reason, const bool warning, const int line)
1807 {
1808  const auto message = failTraceMessage(reason, line);
1809 
1810  if (warning)
1811  {
1814  (*_current_ray)->setShouldContinue(false);
1815  _should_continue = false;
1816  }
1817  else
1819 }
1820 
1821 const std::vector<NeighborInfo> &
1822 TraceRay::getVertexNeighbors(const Elem * elem, const Node * vertex)
1823 {
1824  traceAssert(elem, "Elem must be valid");
1825  traceAssert(vertex, "Vertex must be valid");
1826 
1827  debugRay("Called getVertexNeighbors() with:");
1828  debugRay(" elem->id() = ", elem->id(), " with centroid ", elem->vertex_average());
1829  debugRay(" vertex->id() = ", vertex->id(), ", at ", (Point)*vertex);
1830 
1831  traceAssert(elem->get_node_index(vertex) != libMesh::invalid_uint, "Doesn't contain node");
1832  traceAssert(elem->is_vertex(elem->get_node_index(vertex)), "Node is not a vertex");
1833 
1835 
1836  // Return the entry if we have it cached
1837  auto search = _vertex_neighbors.find(vertex);
1838  if (search != _vertex_neighbors.end())
1839  return search->second;
1840 
1842 
1843  // Make a new entry
1844  debugRay("Building vertex neighbors");
1845  std::vector<NeighborInfo> & entry =
1846  _vertex_neighbors.emplace(vertex, std::vector<NeighborInfo>()).first->second;
1847 
1848  findNodeNeighbors(elem,
1849  vertex,
1850  _neighbor_set,
1854  entry);
1855 
1856  // Fill the side normals
1857  for (auto & neighbor_info : entry)
1858  for (MooseIndex(neighbor_info._sides.size()) i = 0; i < neighbor_info._sides.size(); ++i)
1859  neighbor_info._side_normals[i] =
1860  _study.getSideNormal(neighbor_info._elem, neighbor_info._sides[i], _tid);
1861 
1862  return entry;
1863 }
1864 
1865 const std::vector<NeighborInfo> &
1866 TraceRay::getVertexNeighbors(const Elem * elem, const unsigned short vertex)
1867 {
1868  traceAssert(vertex < elem->n_vertices(), "Invalid vertex");
1869 
1870  return getVertexNeighbors(elem, elem->node_ptr(vertex));
1871 }
1872 
1873 const std::vector<NeighborInfo> &
1874 TraceRay::getEdgeNeighbors(const Elem * elem,
1875  const std::pair<const Node *, const Node *> & vertices,
1876  const Point & point)
1877 {
1878  traceAssert(elem, "Invalid elem");
1879  traceAssert(vertices.first, "Must be valid");
1880  traceAssert(vertices.second, "Must be valid");
1881 
1882  debugRay("Called getEdgeNeighbors() with:");
1883  debugRay(" elem->id() = ", elem->id(), " with centroid ", elem->vertex_average());
1884  debugRay(" vertices.first = ", vertices.first->id(), " at ", (Point)*vertices.first);
1885  debugRay(" vertices.second = ", vertices.second->id(), " at ", (Point)*vertices.second);
1886  debugRay(" point = ", point);
1887 
1888  traceAssert(elem->get_node_index(vertices.first) != libMesh::invalid_uint,
1889  "Doesn't contain vertex");
1890  traceAssert(elem->get_node_index(vertices.second) != libMesh::invalid_uint,
1891  "Doesn't contain vertex");
1892  traceAssert(isWithinSegment(
1893  (Point)*vertices.first, (Point)*vertices.second, point, LOOSE_TRACE_TOLERANCE),
1894  "Point not within edge");
1895 
1897 
1898  const auto ordered_vertices = vertices.first->id() < vertices.second->id()
1899  ? vertices
1900  : std::make_pair(vertices.second, vertices.first);
1901 
1902  // Look for the entry and build if necessary
1903  std::pair<bool, std::vector<NeighborInfo>> * entry;
1904  auto search = _edge_neighbors.find(ordered_vertices);
1905  if (search != _edge_neighbors.end())
1906  entry = &search->second;
1907  else
1908  {
1909  debugRay("Building edge neighbors");
1911  entry = &_edge_neighbors
1912  .emplace(ordered_vertices, std::make_pair(true, std::vector<NeighborInfo>()))
1913  .first->second;
1914  findEdgeNeighbors(elem,
1915  ordered_vertices.first,
1916  ordered_vertices.second,
1917  _neighbor_set,
1921  entry->second);
1922 
1923  bool all_same_edge = true;
1924  for (auto & neighbor_info : entry->second)
1925  {
1926  traceAssert(neighbor_info._lower_bound <= neighbor_info._upper_bound,
1927  "Bound order incorrect");
1928 
1929  // Fill the side normals
1930  for (MooseIndex(neighbor_info._sides.size()) i = 0; i < neighbor_info._sides.size(); ++i)
1931  neighbor_info._side_normals[i] =
1932  _study.getSideNormal(neighbor_info._elem, neighbor_info._sides[i], _tid);
1933 
1934  // See if the bounds are the same as the target edge
1935  if (neighbor_info._lower_bound != 0 || neighbor_info._upper_bound != 1)
1936  all_same_edge = false;
1937  }
1938  entry->first = all_same_edge;
1939  }
1940 
1941  // Means that all neighbors are not on the exact same edge, so we must
1942  // validate/invalidate based on if the neighbor's edge contains our point
1943  if (!entry->first)
1944  {
1945  const auto edge_length =
1946  ((Point)*ordered_vertices.first - (Point)*ordered_vertices.second).norm();
1947  const auto point_location = ((Point)*ordered_vertices.first - point).norm() / edge_length;
1948  for (auto & info : entry->second)
1949  info._valid = (info._lower_bound - TRACE_TOLERANCE) < point_location &&
1950  point_location < (info._upper_bound + TRACE_TOLERANCE);
1951  }
1952 
1953  return entry->second;
1954 }
1955 
1956 const std::vector<NeighborInfo> &
1957 TraceRay::getEdgeNeighbors(const Elem * elem,
1958  const std::pair<unsigned short, unsigned short> & vertices,
1959  const Point & point)
1960 {
1961  debugRay("Called getEdgeNeighbors(), local index version with:");
1962  debugRay(" vertices.first = ", vertices.first);
1963  debugRay(" vertices.second = ", vertices.second);
1964  traceAssert(vertices.first < elem->n_vertices(),
1965  "Invalid vertex with ray " + std::to_string((*_current_ray)->id()));
1966  traceAssert(vertices.second < elem->n_vertices(), "Invalid vertex");
1967 
1968  return getEdgeNeighbors(
1969  elem, std::make_pair(elem->node_ptr(vertices.first), elem->node_ptr(vertices.second)), point);
1970 }
1971 
1972 const std::vector<NeighborInfo> &
1973 TraceRay::getNeighbors(const Elem * elem, const ElemExtrema & extrema, const Point & point)
1974 {
1975  if (!extrema.atExtrema())
1976  return getPointNeighbors(elem, point);
1977  if (extrema.atVertex())
1978  return getVertexNeighbors(elem, extrema.vertex());
1979  return getEdgeNeighbors(elem, extrema.edgeVertices(), point);
1980 }
1981 
1982 const std::vector<NeighborInfo> &
1983 TraceRay::getPointNeighbors(const Elem * elem, const Point & point)
1984 {
1985  traceAssert(elem, "Invalid elem");
1986 
1987  debugRay("Called getPointNeighbors()");
1988  debugRay(" elem = ", elem->id());
1989  debugRay(" point = ", point);
1990 
1992  _point_neighbor_helper.clear();
1993 
1994  findPointNeighbors(elem,
1995  point,
1996  _neighbor_set,
2001 
2002  // Fill the side normals
2003  for (auto & neighbor_info : _point_neighbor_helper)
2004  for (MooseIndex(neighbor_info._sides.size()) i = 0; i < neighbor_info._sides.size(); ++i)
2005  neighbor_info._side_normals[i] =
2006  _study.getSideNormal(neighbor_info._elem, neighbor_info._sides[i], _tid);
2007 
2008  return _point_neighbor_helper;
2009 }
2010 
2011 void
2013 {
2014  if (result == HIT_FACE)
2015  ++_results[FACE_HITS];
2016  else if (result == HIT_VERTEX)
2017  ++_results[VERTEX_HITS];
2018  else if (result == HIT_EDGE)
2019  ++_results[EDGE_HITS];
2020  else
2021  mooseError("Should not call storeExitsElemResult() with result ", result);
2022 }
2023 
2024 void
2025 TraceRay::onSegment(const std::shared_ptr<Ray> & ray)
2026 {
2027  traceAssert((*_current_ray)->currentElem() == _current_elem, "Ray currentElem() incorrect");
2028  traceAssert((*_current_ray)->currentPoint() == _intersection_point,
2029  "Ray currentPoint() incorrect");
2030  traceAssert((*_current_ray)->currentIncomingSide() == _incoming_side,
2031  "Ray currentIncomingSide() incorrect");
2032 #ifndef NDEBUG
2034  {
2037  "_current_elem does not contain incoming point");
2038 
2041  {
2043  .close_to_point(_intersection_point, LOOSE_TRACE_TOLERANCE),
2044  "Intersected point is not on intersected side");
2045  traceAssert(!_study.sideIsIncoming(
2046  _current_elem, _intersected_side, (*_current_ray)->direction(), _tid),
2047  "Intersected side is not outgoing");
2048  }
2051  {
2053  .close_to_point(_incoming_point, LOOSE_TRACE_TOLERANCE),
2054  "Incoming point is not on incoming side");
2055  if (ray->intersections() != 0 && ray->maxDistance() != 0)
2056  traceAssert(_study.sideIsIncoming(
2057  _current_elem, _incoming_side, (*_current_ray)->direction(), _tid),
2058  "Incoming side is not incoming");
2059  }
2060  }
2061 #endif
2064  "_intersection_distance is incorrect");
2065  traceAssert(_current_subdomain_id == _current_elem->subdomain_id(), "Subdomain incorrect");
2068  "Invalid intersection distance");
2069 
2072 
2073  const auto & rks = _study.currentRayKernels(_tid);
2074  for (auto & rk : rks)
2075  {
2076  rk->onSegment();
2077  postRayTracingObject(ray, rk);
2078  }
2079 }
2080 
2081 void
2082 TraceRay::onBoundary(const std::shared_ptr<Ray> & ray, const bool external)
2083 {
2084  traceAssert(ray->currentPoint().absolute_fuzzy_equals(_intersection_point),
2085  "Ray currentPoint() not set before onBoundary()");
2086 
2087  // Get the RayBCs on bnd_elems
2089 
2090  // Store this information temprorarily because we are going to change it as we
2091  // apply each boundary condition
2092  const auto old_current_elem = _current_elem;
2093  const auto old_intersected_side = _intersected_side;
2094  const auto old_intersected_extrema = _intersected_extrema;
2095  const auto old_subdomain_id = _current_subdomain_id;
2096 
2097  // For each RayBC we found, apply it on the boundaries that we need
2099  {
2100  // First, find the boundaries this RayBC is valid on that are also in _boundary_elems.
2101  // We do this ahead of time so that we can pass in to RayBC::apply if the same
2102  // boundary condition is being appled multiple times on different boundaries.
2103  // This is useful in situations like reflection where multiple reflections are
2104  // necessary at a corner to perfectly reflect.
2105  _on_boundary_apply_index.clear();
2106  for (MooseIndex(_boundary_elems.size()) bnd_elems_i = 0; bnd_elems_i < _boundary_elems.size();
2107  ++bnd_elems_i)
2108  if (rbc->hasBoundary(_boundary_elems[bnd_elems_i].bnd_id))
2109  _on_boundary_apply_index.push_back(bnd_elems_i);
2110 
2111  traceAssert(!_on_boundary_apply_index.empty(), "Must not be empty");
2112 
2113  // Apply the RayBC on each of the relevant boundary elements
2114  for (const auto bnd_elems_i : _on_boundary_apply_index)
2115  {
2116  auto & bnd_elem = _boundary_elems[bnd_elems_i];
2117  _current_elem = bnd_elem.elem;
2118  _current_bnd_id = bnd_elem.bnd_id;
2119  _intersected_side = bnd_elem.side;
2120  _intersected_extrema = bnd_elem.extrema;
2122 
2123  rbc->onBoundary(_on_boundary_apply_index.size());
2124  postRayTracingObject(ray, rbc);
2125  }
2126  }
2127 
2128  // Set this info back now that we're done applying BCs
2129  _current_elem = old_current_elem;
2130  _intersected_side = old_intersected_side;
2131  _intersected_extrema = old_intersected_extrema;
2132  _current_bnd_id = BoundaryInfo::invalid_id;
2133  _current_subdomain_id = old_subdomain_id;
2134 
2135  // When on an external boundary, the Ray must have been changed or killed.
2136  // Otherwise, we don't know what to do with it now! If this didn't happen,
2137  // output a detailed error message.
2138  if (external && !ray->trajectoryChanged() && ray->shouldContinue())
2139  {
2140  std::stringstream oss;
2141  oss << "Don't know what to do with a Ray after it hit an external\n";
2142  oss << "boundary at point " << _intersection_point << "!\n\n";
2143  oss << "When hitting an external RayBC, a Ray must either:\n";
2144  oss << " Be killed by a RayBC\n";
2145  oss << " Have its trajectory changed by the RayBC\n";
2146  oss << "by at least one of the executed RayBCs.\n\n";
2147  oss << "You need to either:\n";
2148  oss << " Kill/change the Ray sooner with RayKernels, internal RayBCs, or a max distance\n";
2149  oss << " Kill/change the Ray on the boundary with a RayBC\n\n";
2150  if (!_on_boundary_ray_bcs.empty())
2151  {
2152  oss << "RayBCs executed that did not kill or change the Ray:\n";
2154  for (const auto & bnd_elem : _boundary_elems)
2155  if (rbc->hasBoundary(bnd_elem.bnd_id))
2156  oss << " " << rbc->typeAndName() << " on boundary " << bnd_elem.bnd_id << " ("
2157  << _mesh.getBoundaryName(bnd_elem.bnd_id) << ")\n";
2158  oss << "\n";
2159  }
2160  bool output_header = false;
2161  for (std::size_t i = 0; i < _boundary_elems.size(); ++i)
2162  {
2163  const auto bnd_id = _boundary_elems[i].bnd_id;
2164  bool found = false;
2166  if (rbc->hasBoundary(bnd_id))
2167  {
2168  found = true;
2169  break;
2170  }
2171 
2172  if (!found)
2173  {
2174  if (!output_header)
2175  {
2176  oss << "Boundaries that did not have any RayBCs:\n";
2177  output_header = true;
2178  }
2179  oss << " " << bnd_id << " (" << _mesh.getBoundaryName(bnd_id) << ")\n";
2180  }
2181  }
2182 
2183  failTrace(oss.str(), _study.tolerateFailure(), __LINE__);
2184  }
2185 }
2186 
2187 Real
2188 TraceRay::subdomainHmax(const Elem * elem) const
2189 {
2190  const auto subdomain_id = elem->subdomain_id();
2191  return subdomain_id == _current_subdomain_id ? _current_subdomain_hmax
2192  : _study.subdomainHmax(subdomain_id);
2193 }
2194 
2195 void
2196 TraceRay::postRayTracingObject(const std::shared_ptr<Ray> & ray, const RayTracingObject * rto)
2197 {
2198  if (!ray->shouldContinue())
2199  {
2200  if (_should_continue)
2201  _should_continue = false;
2202  }
2203  else if (!_should_continue)
2204  failTrace(rto->typeAndName() +
2205  " set a Ray to continue that was previously set to not continue.\n\n" +
2206  "Once a Ray has been set to not continue, its continue status cannot change.",
2207  /* warning = */ false,
2208  __LINE__);
2209 
2210  if (!_should_continue && ray->trajectoryChanged())
2211  failTrace(rto->typeAndName() +
2212  " changed the trajectory of a Ray that was set to not continue,\n" +
2213  "or set a Ray whose trajectory was changed to not continue.",
2214  /* warning = */ false,
2215  __LINE__);
2216 }
bool hasRayKernels(const THREAD_ID tid)
Whether or not there are currently any active RayKernel objects.
const std::vector< NeighborInfo > & getPointNeighbors(const Elem *elem, const Point &point)
Get the point neighbors.
Definition: TraceRay.C:1983
static const unsigned short invalid_side
Identifier for an invalid side index.
bool stationary() const
Definition: TraceData.h:59
TracePointData & lastPoint()
Definition: TraceData.h:57
bool withinExtremaOnSide(const Elem *const elem, const Point &point, const unsigned short side, const unsigned int dim, ElemExtrema &extrema)
Determines if a point is within an Elem&#39;s extrema (at vertex/within edge) on a side.
const std::vector< NeighborInfo > & getEdgeNeighbors(const Elem *elem, const std::pair< const Node *, const Node *> &vertices, const Point &point)
Get the neighbors at an edge.
RayTracingStudy & _study
The RayTracingStudy.
Definition: TraceRay.h:436
ElemType
virtual void postOnSegment(const THREAD_ID tid, const std::shared_ptr< Ray > &ray)
Called at the end of a Ray segment.
ExitsElemResult exitsElem(const Elem *elem, const ElemType elem_type, const unsigned short incoming_side, Point &intersection_point, unsigned short &intersected_side, ElemExtrema &intersected_extrema, Real &intersection_distance, const Point *normals)
Determines if _current_ray moving in direction _direction exits elem.
unsigned short _current_elem_n_sides
The number of sides on the current elem, used to avoid elem->n_sides() virtual calls.
Definition: TraceRay.h:467
std::unordered_map< std::pair< const Node *, const Node * >, std::pair< bool, std::vector< NeighborInfo > > > _edge_neighbors
The cached edge neighbors.
Definition: TraceRay.h:504
unsigned short vertex() const
Definition: ElemExtrema.h:96
const std::vector< std::vector< BoundaryID > > & getInternalSidesets(const Elem *elem) const
Get the internal sidesets (that have RayBC(s)) for each side for a given element. ...
ElemExtrema _intersected_extrema
The work point for the intersected vertex/edge vertices of the current Ray, if any.
Definition: TraceRay.h:480
void onTrajectoryChanged(const std::shared_ptr< Ray > &ray)
Called when a Ray&#39;s trajectory changes.
Definition: TraceRay.C:1695
bool segmentsOnCacheTraces() const
Whether or not to cache individual element segments when _cache_traces = true.
static const unsigned short invalid_vertex
Identifier for an invalid vertex index.
bool withinEdgeOnSide(const Elem *const elem, const Point &point, const unsigned short side, ElemExtrema &extrema)
Determines if a point is within an edge on the side of an element.
void findExternalBoundarySide(unsigned short &boundary_side, ElemExtrema &boundary_extrema, const Elem *&boundary_elem)
Finds (if any) an element side that is on the boundary and is outgoing at _intersection_point that is...
Definition: TraceRay.C:950
void applyOnExternalBoundary(const std::shared_ptr< Ray > &ray)
Gets and applies external boundary conditions in _current_elem on side _intersected_side at _intersec...
Definition: TraceRay.C:746
void findPointNeighbors(const Elem *const elem, const Point &point, MooseUtils::StaticallyAllocatedSet< const Elem *, MAX_POINT_NEIGHBORS > &neighbor_set, MooseUtils::StaticallyAllocatedSet< const Elem *, MAX_POINT_NEIGHBORS > &untested_set, MooseUtils::StaticallyAllocatedSet< const Elem *, MAX_POINT_NEIGHBORS > &next_untested_set, std::vector< const Elem *> active_neighbor_children, std::vector< NeighborInfo > &info)
Rewrite of the find_point_neighbors function in libMesh, instead using a statically allocated set: re...
bool hasSameLevelActiveElems() const
Whether or not the mesh has active elements of the same level.
bool absoluteFuzzyEqual(const T &var1, const T2 &var2, const T3 &tol=libMesh::TOLERANCE *libMesh::TOLERANCE)
Mesh * _debug_mesh
Definition: TraceRay.h:533
std::vector< std::size_t > _on_boundary_apply_index
Reusable for which boundary elements to apply for a specific RayBC in onBoundary() ...
Definition: TraceRay.h:522
const unsigned int invalid_uint
QUAD8
unsigned int get_node_index(const Node *node_ptr) const
std::string get_info() const
HEX8
libMesh::ElemType _current_elem_type
The current elem type (constant on subdomain), used to avoid elem->type() calls.
Definition: TraceRay.h:465
T & getMesh(MooseMesh &mesh)
function to cast mesh
Definition: SCM.h:35
void onSegment(const std::shared_ptr< Ray > &ray)
Called on a single segment traced by a Ray.
Definition: TraceRay.C:2025
MPI_Info info
void skip_partitioning(bool skip)
std::string failTraceMessage(const std::string &reason, const int line=-1)
Creates a useful error string with current tracing information.
Definition: TraceRay.C:1758
void mooseError(Args &&... args)
void onContinueTrace(const std::shared_ptr< Ray > &)
Called when a Ray is continuing to trace after segment.
Definition: TraceRay.C:1655
unsigned int _debug_node_count
Definition: TraceRay.h:535
void onSubdomainChanged(const std::shared_ptr< Ray > &ray, const bool same_ray)
Called when the subdomain changes.
Definition: TraceRay.C:1724
EDGE4
const double tol
bool isWithinSegment(const Point &segment1, const Point &segment2, const Point &point, const Real tolerance=TRACE_TOLERANCE)
Checks whether or not a point is within a line segment.
TET10
void mooseWarning(Args &&... args)
virtual bool has_affine_map() const
Base class for a MooseObject used in ray tracing.
MooseUtils::StaticallyAllocatedSet< const Elem *, MAX_POINT_NEIGHBORS > _neighbor_set
Definition: TraceRay.h:508
const std::string & getBoundaryName(BoundaryID boundary_id)
bool _valid
Whether or not this neighbor is valid (needed for neighbors that span an edge)
Definition: NeighborInfo.h:69
PRISM15
void possiblyAddToBoundaryElems(const Elem *elem, const unsigned short side, const std::vector< BoundaryID > &bnd_ids, const ElemExtrema &extrema)
Helper for possibly storing boundary information in _boundary_elems, which is storage for boundary el...
Definition: TraceRay.C:917
Base object for the RayKernel syntax.
Definition: RayKernelBase.h:27
MeshBase & mesh
virtual void reinitSegment(const Elem *elem, const Point &start, const Point &end, const Real length, THREAD_ID tid)
Reinitialize objects for a Ray segment for ray tracing.
bool withinEdge(const Elem *elem, const Point &point, ElemExtrema &extrema, const Real tolerance=TRACE_TOLERANCE)
Determines if a point is within an edge on an element.
Helper for defining if at an element&#39;s edge, vertex, or neither.
Definition: ElemExtrema.h:25
virtual bool is_node_on_side(const unsigned int n, const unsigned int s) const=0
const Real LOOSE_TRACE_TOLERANCE
Looser tolerance for use in error checking in difficult situations.
Definition: TraceRayTools.h:50
const std::vector< RayKernelBase * > & currentRayKernels(THREAD_ID tid) const
Gets the current RayKernels for a thread, which are set in segmentSubdomainSetup() ...
Parallel::Communicator _debug_comm
Definition: TraceRay.h:534
HEX20
void boundary_ids(const Node *node, std::vector< boundary_id_type > &vec_to_fill) const
bool isInvalid() const
Definition: ElemExtrema.h:48
unsigned short atVertexOnSide(const Elem *elem, const Point &point, const unsigned short side)
Determines if a point is at a vertex on the side of en element.
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
Real rayMaxDistance() const
Max distance any Ray can travel.
Struct for containing the necessary information about a cached neighbor for ray tracing.
Definition: NeighborInfo.h:27
static const libMesh::Real invalid_distance
Identifier for an invalid distance.
bool isValid(const Elem *const elem, const Point &point) const
Definition: ElemExtrema.C:49
ElemExtrema _last_intersected_extrema
The intersected vertex/edge vertices for the previous intersection, if any.
Definition: TraceRay.h:482
const unsigned int _dim
The mesh dimension.
Definition: TraceRay.h:440
void postRayTracingObject(const std::shared_ptr< Ray > &ray, const RayTracingObject *rto)
Called after executing a RayTracingObject (RayBCs and RayKernels)
Definition: TraceRay.C:2196
bool _backface_culling
Whether or not to use element normals for backface culling.
Definition: TraceRay.h:449
TRI3
std::vector< RayData > _aux_data
The aux data on the Ray after this segment is traced (optional)
Definition: TraceData.h:36
const BoundingBox & boundingBox() const
Get the nodal bounding box for the domain.
void onBoundary(const std::shared_ptr< Ray > &ray, const bool external)
Called when a Ray hits a boundary.
Definition: TraceRay.C:2082
QUAD4
Real subdomainHmax(const SubdomainID subdomain_id) const
Get the cached hmax for all elements in a subdomain.
const Real TRACE_TOLERANCE
The standard tolerance to use in tracing.
Definition: TraceRayTools.h:48
void trace(const std::shared_ptr< Ray > &ray)
Traces a ray.
Definition: TraceRay.C:1019
const std::vector< unsigned short > _sides
The sides on the element that the neighboring portion is contained in.
Definition: NeighborInfo.h:61
bool isRectangularDomain() const
Whether or not the domain is rectangular (if it is prefectly encompassed by its bounding box) ...
libMesh::ElemSideBuilder _elem_side_builder
Helper for building element sides without excessive allocation.
Definition: TraceRay.h:517
const std::pair< unsigned short, unsigned short > & edgeVertices() const
Definition: ElemExtrema.h:104
Point _incoming_point
The incoming point of the current Ray.
Definition: TraceRay.h:469
const Point * _current_normals
The normals for the current element for backface culling (pointer to the first normal - optional) ...
Definition: TraceRay.h:492
INVALID_ELEM
bool _exits_elem
Whether or not the current trace exits an element.
Definition: TraceRay.h:489
bool verifyTraceIntersections() const
Whether or not trace verification is enabled in devel/dbg modes.
unsigned short _incoming_side
The incoming side of the current Ray.
Definition: TraceRay.h:471
bool verifyRays() const
Whether or not to verify if Rays have valid information before being traced.
std::vector< BoundaryID > _boundary_ids
Reusable vector for calling _boundary_info.boundary_ids()
Definition: TraceRay.h:495
Real subdomainHmax(const Elem *elem) const
Get the approximate subdomain hmax for an element.
Definition: TraceRay.C:2188
std::vector< Point > _side_normals
The normals of each side in _sides.
Definition: NeighborInfo.h:63
std::set< RayKernelBase * > _old_ray_kernels
Helper for avoiding calling preTrace() on the same RayKernel multiple times.
Definition: TraceRay.h:530
virtual bool contains_point(const Point &p, Real tol=TOLERANCE) const
void findNodeNeighbors(const Elem *const elem, const Node *const node, MooseUtils::StaticallyAllocatedSet< const Elem *, MAX_POINT_NEIGHBORS > &neighbor_set, MooseUtils::StaticallyAllocatedSet< const Elem *, MAX_POINT_NEIGHBORS > &untested_set, MooseUtils::StaticallyAllocatedSet< const Elem *, MAX_POINT_NEIGHBORS > &next_untested_set, std::vector< const Elem *> active_neighbor_children, std::vector< NeighborInfo > &info)
dof_id_type id() const
TET4
bool auxDataOnCacheTraces() const
Whether or not to store the Ray aux data on the cached Ray traces.
char ** sides
unsigned int which_neighbor_am_i(const Elem *e) const
TRI6
const processor_id_type _pid
The processor id.
Definition: TraceRay.h:444
void preExecute()
Should be called immediately before calling any traces.
Definition: TraceRay.C:61
const Elem *const _elem
The element.
Definition: NeighborInfo.h:59
MooseMesh & _mesh
The MooseMesh.
Definition: TraceRay.h:438
bool _has_ray_kernels
Whether or not the RayTracingStudy has any RayKernels.
Definition: TraceRay.h:525
std::vector< unsigned long long int > _results
Results over all of the local traces, indexed by TraceRayResult.
Definition: TraceRay.h:514
ExitsElemResult moveThroughNeighbor(const NeighborInfo &neighbor_info, unsigned short &incoming_side, Point &intersection_point, unsigned short &intersected_side, ElemExtrema &intersected_extrema, Real &intersection_distance)
Sees if a Ray can move through a neighbor (vertex/edge/point)
Definition: TraceRay.C:698
const Elem * _last_elem
The last element the current Ray was traced in.
Definition: TraceRay.h:459
ExitsElemResult moveThroughNeighbors(const std::vector< NeighborInfo > &neighbors, const Elem *last_elem, const Elem *&best_elem, unsigned short &best_elem_incoming_side)
Moves the Ray though neighbors (vertex/edge/point)
Definition: TraceRay.C:585
bool rayDependentSubdomainSetup() const
Whether or not to use Ray dependent subdomain setup.
TraceData * _current_cached_trace
The TraceData for the current cached trace (if any)
Definition: TraceRay.h:452
HEX27
OStreamProxy err(std::cerr)
void onCompleteTrace(const std::shared_ptr< Ray > &ray)
Called when a Ray is finished tracing (whenever !ray->shouldContinue())
Definition: TraceRay.C:1627
std::string typeAndName() const
TraceRay(RayTracingStudy &study, const THREAD_ID tid)
Definition: TraceRay.C:47
auto norm(const T &a) -> decltype(std::abs(a))
TET14
bool absolute_fuzzy_equals(const TypeVector< Real > &rhs, Real tol=TOLERANCE) const
std::vector< const Elem * > _neighbor_active_neighbor_children
Definition: TraceRay.h:511
bool sideIsNonPlanar(const Elem *elem, const unsigned short s) const
Whether or not the side on elem elem is non-planar.
std::vector< RayBoundaryConditionBase * > _on_boundary_ray_bcs
Reusable for getting the RayBCs in onBoundary()
Definition: TraceRay.h:520
const std::shared_ptr< Ray > * _current_ray
The current ray being traced.
Definition: TraceRay.h:455
void addPoint(const libMesh::Point &point)
Definition: TraceData.h:55
virtual const Point * getElemNormals(const Elem *, const THREAD_ID)
Gets the outward normals for a given element.
const Elem * _current_elem
The element the current Ray is being traced in.
Definition: TraceRay.h:457
bool atExtrema() const
Definition: ElemExtrema.h:44
bool _is_rectangular_domain
Whether or not the domain is rectangular (defined perfectly by its bounding box)
Definition: TraceRay.h:527
virtual unsigned int n_sides() const=0
TraceData & initThreadedCachedTrace(const std::shared_ptr< Ray > &ray, THREAD_ID tid)
Initialize a Ray in the threaded cached trace map to be filled with segments.
const Elem * neighbor_ptr(unsigned int i) const
virtual const Point & getSideNormal(const Elem *elem, const unsigned short side, const THREAD_ID tid)
Get the outward normal for a given element side.
void setVertex(const unsigned short vertex)
Sets the "at vertex" state.
Definition: ElemExtrema.h:118
virtual bool close_to_point(const Point &p, Real tol) const
const std::vector< NeighborInfo > & getNeighbors(const Elem *elem, const ElemExtrema &extrema, const Point &point)
Get the point/vertex/edge neighbors depending on extrema.
Definition: TraceRay.C:1973
virtual unsigned int n_vertices() const=0
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
bool currentlyPropagating() const
Whether or not the study is propagating (tracing Rays)
EDGE2
subdomain_id_type subdomain_id() const
std::vector< TraceRayBndElement > _boundary_elems
Boundary elements that need RayBCs to be applied.
Definition: TraceRay.h:497
static const std::string message
void invalidate()
Invalidates the current state.
Definition: ElemExtrema.h:87
virtual unsigned short dim() const=0
const Node * node_ptr(const unsigned int i) const
void getRayBCs(std::vector< RayBoundaryConditionBase *> &result, BoundaryID id, THREAD_ID tid)
Fills the active RayBCs associated with this study and a boundary into result.
PYRAMID5
virtual void preTrace(const THREAD_ID, const std::shared_ptr< Ray > &)
Called at the beginning of a trace for a ray.
const THREAD_ID _tid
The thread id.
Definition: TraceRay.h:446
virtual bool is_vertex(const unsigned int i) const=0
virtual void segmentSubdomainSetup(const SubdomainID subdomain, const THREAD_ID tid, const RayID ray_id)
Setup for on subdomain change or subdomain AND ray change during ray tracing.
void applyOnInternalBoundary(const std::shared_ptr< Ray > &ray)
Gets and applies internal boundary conditions (if any) from _current_elem, _last_elem, and any other point neighbors that have internal sidesets at _intersection_point.
Definition: TraceRay.C:805
std::vector< RayData > _data
The data on the Ray after this segment is traced (optional)
Definition: TraceData.h:34
MooseUtils::StaticallyAllocatedSet< const Elem *, MAX_POINT_NEIGHBORS > _neighbor_next_untested_set
Definition: TraceRay.h:510
std::unordered_map< const Node *, std::vector< NeighborInfo > > _vertex_neighbors
The cached vertex neighbors.
Definition: TraceRay.h:500
void failTrace(const std::string &reason, const bool warning, const int line=-1)
Specialized mooseError for a failed Ray trace with detailed information regarding the trace...
Definition: TraceRay.C:1806
MooseUtils::StaticallyAllocatedSet< const Elem *, MAX_POINT_NEIGHBORS > _neighbor_untested_set
Definition: TraceRay.h:509
Real _current_subdomain_hmax
The current subdomain hmax.
Definition: TraceRay.h:463
TRI7
ExitsElemResult
Enum for the different exit results for exitElem()
Definition: TraceRay.h:257
bool atVertex() const
Definition: ElemExtrema.h:56
QUAD9
unsigned short _intersected_side
The work point for the intersected side of the current Ray.
Definition: TraceRay.h:478
static const libMesh::Point invalid_point(invalid_distance, invalid_distance, invalid_distance)
Identifier for an invalid point.
PYRAMID13
SubdomainID _current_subdomain_id
The current SubdomainID.
Definition: TraceRay.h:461
void findEdgeNeighbors(const Elem *const elem, const Node *const node1, const Node *const node2, MooseUtils::StaticallyAllocatedSet< const Elem *, MAX_POINT_NEIGHBORS > &neighbor_set, MooseUtils::StaticallyAllocatedSet< const Elem *, MAX_POINT_NEIGHBORS > &untested_set, MooseUtils::StaticallyAllocatedSet< const Elem *, MAX_POINT_NEIGHBORS > &next_untested_set, std::vector< const Elem *> active_neighbor_children, std::vector< NeighborInfo > &info)
bool _last
Whether or not this was the last set of segments for this Ray.
Definition: TraceData.h:76
bool tolerateFailure() const
Whether or not to tolerate failure.
PRISM6
PRISM18
const BoundaryInfo & _boundary_info
The BoundaryInfo for the mesh.
Definition: TraceRay.h:442
bool onBoundingBoxBoundary(const BoundingBox &bbox, const Point &point, const unsigned int dim, const Real tolerance)
Whether or not point is on the boundary (min/max) of bbox.
BoundaryID _current_bnd_id
The current BoundaryID (used when calling RayBoundaryConditionBase::onBoundary()) ...
Definition: TraceRay.h:486
unsigned short atVertex(const Elem *elem, const Point &point)
Determines if a point is at a vertex of an element.
const std::vector< NeighborInfo > & getVertexNeighbors(const Elem *elem, const Node *vertex)
Gets the neighbors at a vertex.
Base class for the RayBC syntax.
void continueTraceOffProcessor(const std::shared_ptr< Ray > &ray)
Sets up a ray to continue tracing off processor.
Definition: TraceRay.C:1670
bool active() const
EDGE3
Real _intersection_distance
The work point for the intersection distance of the current Ray.
Definition: TraceRay.h:484
processor_id_type processor_id() const
bool dataOnCacheTraces() const
Whether or not to store the Ray data on the cached Ray traces.
virtual ElemType type() const=0
void meshChanged()
Called on mesh change.
Definition: TraceRay.C:75
const Point & point(const unsigned int i) const
bool atEdge() const
Definition: ElemExtrema.h:71
std::vector< NeighborInfo > _point_neighbor_helper
Reusable for building neighbors.
Definition: TraceRay.h:507
void storeExitsElemResult(const ExitsElemResult result)
Stores the result given by an intersection in _results as necessary.
Definition: TraceRay.C:2012
bool _should_continue
Whether or not the current Ray should continue.
Definition: TraceRay.h:473
Point vertex_average() const
Real domainMaxLength() const
Get the inflated maximum length across the domain.
bool sideIsIncoming(const Elem *const elem, const unsigned short side, const Point &direction, const THREAD_ID tid)
Whether or not side is incoming on element elem in direction direction.
const Elem * getActiveNeighbor(const Elem *elem, const unsigned short side, const Point &point)
Get the active neighbor on side of elem that contains point.
bool hasInternalSidesets() const
Whether or not the local mesh has internal sidesets that have RayBCs on them.
unsigned int THREAD_ID
Base class for Ray tracing studies that will generate Rays and then propagate all of them to terminat...
virtual bool shouldCacheTrace(const std::shared_ptr< Ray > &) const
Virtual that allows for selection in if a Ray should be cached or not (only used when _cache_traces)...
PYRAMID14
Point _intersection_point
The work point for the intersection of the current Ray.
Definition: TraceRay.h:476