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