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