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