https://mooseframework.inl.gov
Loading...
Searching...
No Matches
Public Member Functions | Private Member Functions | Private Attributes | List of all members
AdaptiveRayContainmentCheck Class Referencefinal

Ray-casting point-in-solid engine over a closed surface mesh represented as a collection of SurfaceElement wrappers. More...

#include <AdaptiveRayContainmentCheck.h>

Public Member Functions

 AdaptiveRayContainmentCheck (const std::vector< std::unique_ptr< SurfaceElement > > &bd_elements, const std::vector< Point > &centroids, const SurfaceGeometry::RayDirectionOptions &ray_options, const Real eps_on_surface=libMesh::TOLERANCE, const int leaf_max_size=10, const FileName &obb_file_name="", const FileName &ray_file_name="", const libMesh::Parallel::Communicator *comm=nullptr)
 
SurfaceGeometry::SurfaceSide sideness (const Point &p) const
 Main function: Determine if a point is inside the geometry.
 
const Point & rayDirection () const
 The resolved ray direction actually used for shooting: the (normalized) user direction for a user-selected ray, or the PCA-selected direction for an auto ray.
 

Private Member Functions

bool rayIntersectGeometry (const Point &ray_start, const Point &ray_end, const SurfaceElement *elem) const
 Ray-element intersection (e.g., ray-line for 2D, ray-triangle for 3D)
 
bool isOutsideBoundingBox (const Point &query_point) const
 Check if point is outside global bounding box.
 
bool isOutsideRayBBox (const Point &orig, const Point &dir, const Ball &ball) const
 Check if element center is outside ray bounding box.
 
bool isOutsideBoundingRegion (const Point &orig, const Point &dir, const Ball &ball) const
 Check if element center is outside ray bounding circle/sphere.
 
BoundingBox computeGlobalBoundingBox ()
 Compute the global bounding box of all boundary elements.
 
void preparePCASVD ()
 
void initializeRayDirection ()
 Finalizes the ray direction and the matching bounding box.
 
std::optional< SurfaceGeometry::SurfaceSidesidenessFromRayPair (const Point &p, const std::array< Point, 2 > &ray_starts) const
 Determine sideness from a pair of opposite rays.
 
bool isOnSurface (const Point &p) const
 True if p lies on the surface (within _eps_on_surface), i.e.
 
int countCrossings (const Point &ray_start, const Point &ray_end, const bool use_primary_direction=true) const
 Count how many times the segment from ray_start to ray_end crosses the surface.
 
template<typename CrossingTest >
int countFilteredCrossings (const Point &ray_start, const Point &ray_end, const bool use_primary_direction, CrossingTest is_crossing) const
 Shared traversal for the 2D and 3D crossing counts: walk the candidate elements (KD-tree candidates for the primary direction, all elements otherwise), apply the ray-bbox and bounding-region pruning, and count those for which is_crossing(surface) is true.
 
int countCrossings2D (const Point &ray_start, const Point &ray_end, const bool use_primary_direction) const
 2D crossing count using a half-open side-based crossing rule: an edge is counted when its two endpoints lie on strictly opposite sides of the ray line (a vertex or collinear edge exactly on the line is thus counted consistently, with no tolerance) and the crossing is on the ray_start side of the query point.
 
Point rayStartOutsideAABB (const Point &point, const Point &unit_direction, const bool inverted) const
 Ray start strictly outside the global AABB along unit_direction, for any direction.
 
Point rayStartOutsideOBB (const Point &point, const Point &ray_direction, const unsigned int obb_axis, const bool inverted=false) const
 Computes the starting point of an OBB-based ray (auto/PCA policy) for a given query point.
 
Point projectPointOntoPlane (const Point &point_to_project, const Point &plane_point, const Point &plane_normal) const
 Orthogonally project point_to_project onto the plane defined by plane_point and unit normal plane_normal.
 
void buildObbKdtreeAndMaxProjectedDiagonal (const Real expand_box_length)
 Constructs an oriented bounding box (OBB) using the results of PCA and the KD-tree.
 
std::vector< unsigned intcollectCandidateElementIDs (const Point &query_point) const
 Use the kd-tree to collect candidate element IDs to check intersections.
 

Private Attributes

Point _centroid_nodal_points
 The centroid of the boundary elements' node points (prepare inside this class).
 
std::vector< Point > _projected_centroids
 Projected centroids of the elements in the boundary mesh.
 
std::unique_ptr< KDTree_kd_tree
 The KDTree is constructed using the projected centroids of the elements in the boundary mesh.
 
Real _max_projected_diag_length
 The maximum diagonal length of the projected bounding box from the boundary elements.
 
const std::vector< std::unique_ptr< SurfaceElement > > & _bd_elements
 pass into the constructor for the surface elements
 
const std::vector< Point > & _centroids
 pass into the constructor for the surface element centroids
 
int _dim = -1
 The dimension of the embedding mesh.
 
std::size_t _num_elements = 0
 The number of elements in the boundary mesh.
 
Point _ray_direction
 Ray shooting direction.
 
Real _eps_on_surface
 Epsilon value for checking if a point is on the surface of the geometry.
 
int _leaf_max_size
 Configures KDTree leaf node size for performance tuning.
 
FileName _obb_file_name
 The file name for the OBB.
 
FileName _ray_file_name
 The file name for the ray.
 
const libMesh::Parallel::Communicator_comm = nullptr
 Communicator used only for writing the debug OBB/ray mesh files.
 
OrientedBoundingBox _obb_bounds
 The oriented bounding box (OBB).
 
BoundingBox _bounds
 The bounding box AABB.
 
bool _auto_ray_direction = false
 Whether the ray direction is auto-selected via PCA (true) or user-selected (false).
 
bool _build_obb = false
 When the ray direction is auto-selected (PCA) we build an Oriented Bounding Box (OBB); a user-selected ray uses only the global Axis-Aligned Bounding Box (AABB) instead.
 
Point _plane_origin = Point(0.0, 0.0, 0.0)
 The origin of the plane used to ensure that every projected point is correctly aligned and lies on the same plane.
 
Point _max_variance_vector
 max variance vector
 
Point _second_variance_vector
 second max variance vector
 
Point _min_variance_vector
 min variance vector (only used for 3D)
 

Detailed Description

Ray-casting point-in-solid engine over a closed surface mesh represented as a collection of SurfaceElement wrappers.

The shooting direction follows one of two explicit policies (RayDirectionMode):

Definition at line 36 of file AdaptiveRayContainmentCheck.h.

Constructor & Destructor Documentation

◆ AdaptiveRayContainmentCheck()

AdaptiveRayContainmentCheck::AdaptiveRayContainmentCheck ( const std::vector< std::unique_ptr< SurfaceElement > > &  bd_elements,
const std::vector< Point > &  centroids,
const SurfaceGeometry::RayDirectionOptions ray_options,
const Real  eps_on_surface = libMesh::TOLERANCE,
const int  leaf_max_size = 10,
const FileName &  obb_file_name = "",
const FileName &  ray_file_name = "",
const libMesh::Parallel::Communicator comm = nullptr 
)

Definition at line 19 of file AdaptiveRayContainmentCheck.C.

28 : _bd_elements(bd_elements),
29 _centroids(centroids),
30 _ray_direction(ray_options.direction),
31 _eps_on_surface(eps_on_surface),
32 _leaf_max_size(leaf_max_size),
33 _obb_file_name(obb_file_name),
34 _ray_file_name(ray_file_name),
35 _comm(comm),
36 _plane_origin(Point(0.0, 0.0, 0.0))
37{
38 // Runtime (not mooseAssert) check: this precondition depends on caller-supplied input and
39 // guards the _bd_elements[0] dereference below. mooseAssert is compiled out in opt builds,
40 // which would leave that dereference as out-of-bounds undefined behavior on an empty set.
41 if (_bd_elements.empty())
43 "AdaptiveRayContainmentCheck: boundary elements must not be empty or uninitialized.");
45 _dim = _bd_elements[0]->expectedEmbeddingMeshDim();
46
48
50 {
51 // USER_SPECIFIED policy: use the direction exactly. Validate only that it is usable, then
52 // normalize. Degeneracy (a ray grazing a vertex/edge or tangent to the surface) is the
53 // user's responsibility; the engine never auto-corrects or switches the direction.
54 for (const auto i : make_range(3u))
57 "AdaptiveRayContainmentCheck: a user-selected ray_direction must be finite; got ",
59 ".");
60 if (MooseUtils::absoluteFuzzyEqual(_ray_direction.norm(), 0.0))
61 mooseError("AdaptiveRayContainmentCheck: a user-selected ray_direction must be non-zero.");
62 if (_dim == 2 && !MooseUtils::absoluteFuzzyEqual(_ray_direction(2), 0.0))
63 mooseError("AdaptiveRayContainmentCheck: a user-selected ray_direction for a 2D surface must "
64 "lie in the mesh plane (its z component must be zero); got ",
66 ".");
68 }
69
70 // PCA is only used to auto-select the direction; a user-selected ray never runs it.
74 1e-2 /*safe protect: expanded box length in each direction and both sides*/);
75}
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
if(!dmm->_nl) SETERRQ(PETSC_COMM_WORLD
Point _ray_direction
Ray shooting direction.
FileName _obb_file_name
The file name for the OBB.
Point _plane_origin
The origin of the plane used to ensure that every projected point is correctly aligned and lies on th...
Real _eps_on_surface
Epsilon value for checking if a point is on the surface of the geometry.
int _dim
The dimension of the embedding mesh.
const std::vector< std::unique_ptr< SurfaceElement > > & _bd_elements
pass into the constructor for the surface elements
FileName _ray_file_name
The file name for the ray.
int _leaf_max_size
Configures KDTree leaf node size for performance tuning.
const std::vector< Point > & _centroids
pass into the constructor for the surface element centroids
std::size_t _num_elements
The number of elements in the boundary mesh.
void buildObbKdtreeAndMaxProjectedDiagonal(const Real expand_box_length)
Constructs an oriented bounding box (OBB) using the results of PCA and the KD-tree.
const libMesh::Parallel::Communicator * _comm
Communicator used only for writing the debug OBB/ray mesh files.
bool _auto_ray_direction
Whether the ray direction is auto-selected via PCA (true) or user-selected (false).
@ AUTO_PCA
The engine auto-selects a robust direction via PCA (may use a fallback).
bool isfinite(std::complex< T > a)
IntRange< T > make_range(T beg, T end)

Member Function Documentation

◆ buildObbKdtreeAndMaxProjectedDiagonal()

void AdaptiveRayContainmentCheck::buildObbKdtreeAndMaxProjectedDiagonal ( const Real  expand_box_length)
private

Constructs an oriented bounding box (OBB) using the results of PCA and the KD-tree.

During this process, it also finds the maximum projected diagonal length.

Definition at line 506 of file AdaptiveRayContainmentCheck.C.

507{
508 if (!_centroids.empty())
509 mooseAssert(_centroids.size() >= 3, "Need at least three points.");
510
511 // (a) Prepare KD-tree data (optional) and track PCA-space extents
512
513 // Initialize the ray direction if not set
515
516 // Global min / max along the three PCA axes
517 Real u_min = std::numeric_limits<Real>::max();
518 Real u_max = std::numeric_limits<Real>::lowest();
519 Real v_min = u_min, v_max = u_max;
520 Real w_min = u_min, w_max = u_max;
521
523
525
526 for (const auto i : make_range(_num_elements))
527 {
528 // Per-element KD-tree data
529 {
530 const Point & pt =
531 (!_centroids.empty() ? _centroids[i] : _bd_elements[i]->elem().vertex_average());
532
534
535 if (_dim == 2) // flatten Z in 2-D mode
536 _projected_centroids[i](2) = 0.0;
537
540 _bd_elements[i]->getProjectedBoundingBoxDiagonal(_ray_direction));
541 }
542
543 if (_build_obb)
544 {
545 // Update PCA-space bounding box
546 const Elem & e = _bd_elements[i]->elem();
547
548 for (const auto j : make_range(e.n_nodes()))
549 {
550 const Point d = *(e.node_ptr(j)) - _centroid_nodal_points;
551 const Real u = d * _max_variance_vector;
552 const Real v = d * _second_variance_vector;
553 const Real w = (_dim == 3) ? d * _min_variance_vector : 0.0;
554
555 u_min = std::min(u_min, u);
556 u_max = std::max(u_max, u);
557 v_min = std::min(v_min, v);
558 v_max = std::max(v_max, v);
559 if (_dim == 3)
560 {
561 w_min = std::min(w_min, w);
562 w_max = std::max(w_max, w);
563 }
564 }
565 }
566 }
567
568 if (_build_obb)
569 {
570 // (b) Build the oriented bounding box (OBB)
571 const Point min_corner =
573 ((_dim == 3) ? w_min * _min_variance_vector : Point()) -
574 expand_box_length * _max_variance_vector - expand_box_length * _second_variance_vector -
575 ((_dim == 3) ? expand_box_length * _min_variance_vector : Point());
576
577 std::vector<std::pair<Point, Point>> axis_pairs{
578 {min_corner,
579 min_corner + (u_max - u_min) * _max_variance_vector +
580 2 * expand_box_length *
581 _max_variance_vector /*2 because we subtract 1 in min_corner*/},
582
583 {min_corner,
584 min_corner + (v_max - v_min) * _second_variance_vector +
585 2 * expand_box_length * _second_variance_vector}};
586
587 if (_dim == 3)
588 axis_pairs.emplace_back(min_corner,
589 min_corner + (w_max - w_min) * _min_variance_vector +
590 2 * expand_box_length * _min_variance_vector);
591
592 _obb_bounds = OrientedBoundingBox(axis_pairs);
593
594 if (_obb_file_name != "")
595 {
596 if (!_comm)
597 mooseError("A communicator is required to write the OBB mesh file '", _obb_file_name, "'.");
598 std::filesystem::path obb_path(_obb_file_name.c_str());
599 _obb_bounds.writeMesh(obb_path, *_comm);
600 }
601
602 if (_ray_file_name != "")
603 {
604 if (!_comm)
605 mooseError("A communicator is required to write the ray mesh file '", _ray_file_name, "'.");
606 std::filesystem::path ray_path(_ray_file_name.c_str());
608 }
609 }
610
611 // (c) Finalise KD-tree
612 _kd_tree = std::make_unique<KDTree>(_projected_centroids, _leaf_max_size);
613}
std::vector< Point > _projected_centroids
Projected centroids of the elements in the boundary mesh.
std::unique_ptr< KDTree > _kd_tree
The KDTree is constructed using the projected centroids of the elements in the boundary mesh.
Point _max_variance_vector
max variance vector
Point projectPointOntoPlane(const Point &point_to_project, const Point &plane_point, const Point &plane_normal) const
Orthogonally project point_to_project onto the plane defined by plane_point and unit normal plane_nor...
Point _second_variance_vector
second max variance vector
Point _centroid_nodal_points
The centroid of the boundary elements' node points (prepare inside this class).
Real _max_projected_diag_length
The maximum diagonal length of the projected bounding box from the boundary elements.
Point _min_variance_vector
min variance vector (only used for 3D)
OrientedBoundingBox _obb_bounds
The oriented bounding box (OBB).
bool _build_obb
When the ray direction is auto-selected (PCA) we build an Oriented Bounding Box (OBB); a user-selecte...
void initializeRayDirection()
Finalizes the ray direction and the matching bounding box.
Oriented bounding box in 2 D or 3 D.
void writeRayAlongShortestAxis(const std::filesystem::path &ray_path, const libMesh::Parallel::Communicator &comm) const
Write a single-EDGE2 mesh representing a "ray" emanating from the box.
void writeMesh(const std::filesystem::path &path, const libMesh::Parallel::Communicator &comm) const
Write the oriented box as a single libMesh element to a mesh file.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const dof_id_type n_nodes

Referenced by AdaptiveRayContainmentCheck().

◆ collectCandidateElementIDs()

std::vector< unsigned int > AdaptiveRayContainmentCheck::collectCandidateElementIDs ( const Point &  query_point) const
private

Use the kd-tree to collect candidate element IDs to check intersections.

Definition at line 616 of file AdaptiveRayContainmentCheck.C.

617{
618 std::vector<unsigned int> elem_ids;
619
620 // KD-tree radius search in projected PCA space
621 Point proj = projectPointOntoPlane(query_point, _plane_origin, _ray_direction);
622 if (_dim == 2)
623 proj(2) = 0.0; // flatten Z for 2-D
624
625 std::vector<nanoflann::ResultItem<std::size_t, Real>> matches;
626 _kd_tree->radiusSearch(proj, _max_projected_diag_length, matches);
627
628 elem_ids.reserve(matches.size());
629 for (const auto & m : matches)
630 elem_ids.push_back(static_cast<unsigned int>(m.first));
631
632 return elem_ids;
633}
void ErrorVector unsigned int

Referenced by countFilteredCrossings(), and isOnSurface().

◆ computeGlobalBoundingBox()

BoundingBox AdaptiveRayContainmentCheck::computeGlobalBoundingBox ( )
private

Compute the global bounding box of all boundary elements.

Definition at line 310 of file AdaptiveRayContainmentCheck.C.

311{
312 const auto & first_elem = _bd_elements[0]->elem();
313 BoundingBox bbox = first_elem.loose_bounding_box();
314
315 for (const auto & bd_elem : _bd_elements)
316 bbox.union_with(bd_elem->elem().loose_bounding_box());
317
318 const Real eps = _eps_on_surface;
319 Point min_pt = bbox.min();
320 Point max_pt = bbox.max();
321
322 for (const auto d : make_range(3u))
323 {
324 min_pt(d) -= eps;
325 max_pt(d) += eps;
326 }
327
328 return BoundingBox(min_pt, max_pt);
329}
int eps(unsigned int i, unsigned int j)
2D version

Referenced by initializeRayDirection().

◆ countCrossings()

int AdaptiveRayContainmentCheck::countCrossings ( const Point &  ray_start,
const Point &  ray_end,
const bool  use_primary_direction = true 
) const
private

Count how many times the segment from ray_start to ray_end crosses the surface.

The primary direction uses KD-tree candidates; PCA fallback directions scan all elements because the KD-tree projection is aligned only with the primary direction. Callers must rule out the on-surface case (via isOnSurface) before interpreting the count.

Definition at line 196 of file AdaptiveRayContainmentCheck.C.

199{
200 // The 2D count uses a half-open side-based crossing rule, which counts a ray passing exactly
201 // through a boundary vertex or along a collinear edge correctly without any tolerance. The 3D
202 // path is unchanged.
203 if (_dim == 2)
204 return countCrossings2D(ray_start, ray_end, use_primary_direction);
205
206 const auto ray_hits_surface = [this, &ray_start, &ray_end](const SurfaceElement * surface)
207 { return rayIntersectGeometry(ray_start, ray_end, surface); };
208
209 return countFilteredCrossings(ray_start, ray_end, use_primary_direction, ray_hits_surface);
210}
int countFilteredCrossings(const Point &ray_start, const Point &ray_end, const bool use_primary_direction, CrossingTest is_crossing) const
Shared traversal for the 2D and 3D crossing counts: walk the candidate elements (KD-tree candidates f...
int countCrossings2D(const Point &ray_start, const Point &ray_end, const bool use_primary_direction) const
2D crossing count using a half-open side-based crossing rule: an edge is counted when its two endpoin...
bool rayIntersectGeometry(const Point &ray_start, const Point &ray_end, const SurfaceElement *elem) const
Ray-element intersection (e.g., ray-line for 2D, ray-triangle for 3D)
Base class for a single surface (boundary) element of a closed surface mesh.

Referenced by sideness(), and sidenessFromRayPair().

◆ countCrossings2D()

int AdaptiveRayContainmentCheck::countCrossings2D ( const Point &  ray_start,
const Point &  ray_end,
const bool  use_primary_direction 
) const
private

2D crossing count using a half-open side-based crossing rule: an edge is counted when its two endpoints lie on strictly opposite sides of the ray line (a vertex or collinear edge exactly on the line is thus counted consistently, with no tolerance) and the crossing is on the ray_start side of the query point.

See countCrossings() for the parameters.

Definition at line 213 of file AdaptiveRayContainmentCheck.C.

216{
217 // The ray goes from ray_start (placed outside the geometry) to the query point p = ray_end, so
218 // the crossings on the segment are exactly those of the half-line from p toward ray_start.
219 const Point & p = ray_end;
220 const Point dir = ray_end - ray_start; // start -> p
221
222 const auto edge_crosses_ray = [&p, &dir](const SurfaceElement * surface)
223 {
224 const Elem & e = surface->elem();
225 const Point a = e.point(0) - p;
226 const Point b = e.point(1) - p;
227
228 // Signed perpendicular position of each edge endpoint relative to the ray line. The strict ">"
229 // on both endpoints is the half-open ("one end closed, one end open") crossing convention: an
230 // endpoint exactly on the ray line is assigned one fixed side, so a vertex shared by two edges
231 // is counted by exactly one of them, an edge collinear with the ray (both sides zero) is
232 // skipped, and a tangential touch cancels to an even count. No tolerance is needed.
233 const Real side_a = dir.cross(a)(2);
234 const Real side_b = dir.cross(b)(2);
235 if ((side_a > 0.0) == (side_b > 0.0))
236 return false;
237
238 // The edge crosses the ray line; keep it only if the crossing lies on the ray_start side of p
239 // (on the segment [ray_start, p]), i.e. opposite the +dir direction that points from start to
240 // p.
241 const Real t = side_a / (side_a - side_b);
242 const Point crossing = a + t * (b - a); // crossing point relative to p
243 return dir * crossing < 0.0;
244 };
245
246 return countFilteredCrossings(ray_start, ray_end, use_primary_direction, edge_crosses_ray);
247}

Referenced by countCrossings().

◆ countFilteredCrossings()

template<typename CrossingTest >
int AdaptiveRayContainmentCheck::countFilteredCrossings ( const Point &  ray_start,
const Point &  ray_end,
const bool  use_primary_direction,
CrossingTest  is_crossing 
) const
private

Shared traversal for the 2D and 3D crossing counts: walk the candidate elements (KD-tree candidates for the primary direction, all elements otherwise), apply the ray-bbox and bounding-region pruning, and count those for which is_crossing(surface) is true.

The per-dimension crossing test is supplied by the caller so the pruning loop lives in exactly one place. Defined in the .C: only the two in-file callers instantiate it.

Definition at line 167 of file AdaptiveRayContainmentCheck.C.

171{
172 const auto candidate_ids =
173 use_primary_direction ? collectCandidateElementIDs(ray_end) : std::vector<unsigned int>{};
174 const auto num_candidates = use_primary_direction ? candidate_ids.size() : _num_elements;
175 const Point segment_direction = ray_end - ray_start;
176
177 int count = 0;
178 for (const auto candidate : make_range(num_candidates))
179 {
180 const auto elem_id = use_primary_direction ? candidate_ids[candidate] : candidate;
181 const auto & surface = _bd_elements[elem_id].get();
182 const auto ball = surface->computeBoundingBall();
183
184 if (isOutsideRayBBox(ray_start, segment_direction, ball))
185 continue;
186 if (isOutsideBoundingRegion(ray_start, segment_direction, ball))
187 continue;
188
189 if (is_crossing(surface))
190 ++count;
191 }
192 return count;
193}
unsigned int count
Definition MortarUtils.C:53
bool isOutsideRayBBox(const Point &orig, const Point &dir, const Ball &ball) const
Check if element center is outside ray bounding box.
bool isOutsideBoundingRegion(const Point &orig, const Point &dir, const Ball &ball) const
Check if element center is outside ray bounding circle/sphere.
std::vector< unsigned int > collectCandidateElementIDs(const Point &query_point) const
Use the kd-tree to collect candidate element IDs to check intersections.

Referenced by countCrossings(), and countCrossings2D().

◆ initializeRayDirection()

void AdaptiveRayContainmentCheck::initializeRayDirection ( )
private

Finalizes the ray direction and the matching bounding box.

Finalize the ray direction (auto -> PCA, user -> as given) and its bounding box.

For an auto ray it sets _ray_direction to the PCA-selected direction and requests the OBB; for a user-selected ray it retains the user's direction and builds the global AABB.

Definition at line 376 of file AdaptiveRayContainmentCheck.C.

377{
379 {
380 // Auto ray: adopt the PCA-selected direction and use the oriented bounding box.
382 _build_obb = true;
383 }
384 else
385 // User-selected axis-aligned ray: keep the user's (normalized) direction and use a
386 // global axis-aligned bounding box.
388}
BoundingBox computeGlobalBoundingBox()
Compute the global bounding box of all boundary elements.
BoundingBox _bounds
The bounding box AABB.

Referenced by buildObbKdtreeAndMaxProjectedDiagonal().

◆ isOnSurface()

bool AdaptiveRayContainmentCheck::isOnSurface ( const Point &  p) const
private

True if p lies on the surface (within _eps_on_surface), i.e.

some candidate element contains it. This is a property of p alone, independent of any ray direction.

Definition at line 157 of file AdaptiveRayContainmentCheck.C.

158{
159 for (const auto elem_id : collectCandidateElementIDs(p))
160 if (_bd_elements[elem_id].get()->elem().contains_point(p, _eps_on_surface))
161 return true;
162 return false;
163}
const Elem & get(const ElemType type_in)

Referenced by sideness().

◆ isOutsideBoundingBox()

bool AdaptiveRayContainmentCheck::isOutsideBoundingBox ( const Point &  query_point) const
private

Check if point is outside global bounding box.

Definition at line 259 of file AdaptiveRayContainmentCheck.C.

260{
261 return (_build_obb) ? !_obb_bounds.contains(query_point, _eps_on_surface)
262 : !_bounds.contains_point(query_point);
263}
bool contains(const Point &pt, const Real tolerance=libMesh::TOLERANCE) const
Test whether a point lies inside or on the box.

Referenced by sideness().

◆ isOutsideBoundingRegion()

bool AdaptiveRayContainmentCheck::isOutsideBoundingRegion ( const Point &  orig,
const Point &  dir,
const Ball ball 
) const
private

Check if element center is outside ray bounding circle/sphere.

Definition at line 290 of file AdaptiveRayContainmentCheck.C.

293{
294 const auto & center = ball.center();
295 const auto radius = ball.radius();
296
297 const auto w = center - orig;
298
299 Real b = (w * dir) / (dir * dir);
300 Point Pb = orig + b * dir;
301
302 Real distance_squared = 0.0;
303 for (const auto i : make_range(_dim))
304 distance_squared += Utility::pow<2>(Pb(i) - center(i));
305
306 return (distance_squared > radius * radius);
307}
Point center
Definition MortarUtils.C:58
const libMesh::Point & center() const
Definition Ball.h:41
libMesh::Real radius() const
Definition Ball.h:42
T pow(const T &x)
const Real radius

Referenced by countFilteredCrossings().

◆ isOutsideRayBBox()

bool AdaptiveRayContainmentCheck::isOutsideRayBBox ( const Point &  orig,
const Point &  dir,
const Ball ball 
) const
private

Check if element center is outside ray bounding box.

Definition at line 266 of file AdaptiveRayContainmentCheck.C.

269{
270 Point lb, ub;
271 const auto & center = ball.center();
272 const Real radius = ball.radius();
273
274 for (const auto i : make_range(_dim))
275 {
276 lb(i) = std::min(orig(i), orig(i) + dir(i)) - radius;
277 ub(i) = std::max(orig(i), orig(i) + dir(i)) + radius;
278 }
279
280 for (const auto i : make_range(_dim))
281 {
282 if (center(i) < lb(i) || center(i) > ub(i))
283 return true;
284 }
285
286 return false;
287}

Referenced by countFilteredCrossings().

◆ preparePCASVD()

void AdaptiveRayContainmentCheck::preparePCASVD ( )
private

Definition at line 430 of file AdaptiveRayContainmentCheck.C.

431{
432 Point centroid_sum;
433
434 std::vector<Point> nodal_points;
435 for (const auto & elem : _bd_elements)
436 {
437 const auto & e = elem->elem();
438 for (const auto i : make_range(e.n_nodes()))
439 {
440 const Node * node = e.node_ptr(i);
441 mooseAssert(node, "Node pointer is null!");
442 nodal_points.push_back(*node);
443 centroid_sum += *node;
444 }
445 }
446
447 const unsigned int N = nodal_points.size();
448 mooseAssert(N >= 3, "At least 3 points required");
449
450 // (a) Compute the centroid
451 _centroid_nodal_points = centroid_sum / static_cast<Real>(N);
452
453 // (b) Build the mean-centered matrix X (N x 3)
454 DenseMatrix<Real> X(N, 3);
455 for (const auto i : make_range(N))
456 {
457 const Point d = nodal_points[i] - _centroid_nodal_points;
458 X(i, 0) = d(0);
459 X(i, 1) = d(1);
460 X(i, 2) = d(2);
461 }
462
463 // (c) Perform SVD: X = U * sigma * V^T
464 DenseVector<Real> sigma;
465 DenseMatrix<Real> U, VT;
466 X.svd(sigma, U, VT); // VT is 3x3, each row is a principal direction
467
468 // (d) Extract principal directions
469 _max_variance_vector = Point(VT(0, 0), VT(0, 1), VT(0, 2)); // max variance
470 _second_variance_vector = Point(VT(1, 0), VT(1, 1), VT(1, 2)); // second largest variance
471 _min_variance_vector = Point(VT(2, 0), VT(2, 1), VT(2, 2)); // min variance
472
473 // (e) normalize them to be safe (unit() returns a normalized copy)
477
478 // (f) Canonicalize the sign of each principal direction. SVD singular vectors
479 // are only defined up to sign, and LAPACK can return opposite signs on
480 // different platforms or versions. Because the auto-selected ray direction is
481 // one of these vectors, an unstable sign makes the in-out classification of
482 // borderline elements non-reproducible across platforms. Fix a deterministic
483 // convention: make the largest-magnitude component positive (ties broken by
484 // the lowest index).
485 auto canonicalize_sign = [](Point & v)
486 {
487 unsigned int i_max = 0;
488 for (const auto i : make_range(1, 3))
489 if (std::abs(v(i)) > std::abs(v(i_max)))
490 i_max = i;
491 if (v(i_max) < 0.0)
492 v *= -1.0;
493 };
494 canonicalize_sign(_max_variance_vector);
495 canonicalize_sign(_second_variance_vector);
496 canonicalize_sign(_min_variance_vector);
497
498 mooseAssert(
499 MooseUtils::absoluteFuzzyEqual(_max_variance_vector * _second_variance_vector, 0.0) &&
500 MooseUtils::absoluteFuzzyEqual(_max_variance_vector * _min_variance_vector, 0.0) &&
501 MooseUtils::absoluteFuzzyEqual(_second_variance_vector * _min_variance_vector, 0.0),
502 "Principal directions are not orthogonal.");
503}
MetaPhysicL::DualNumber< V, D, asd > abs(const MetaPhysicL::DualNumber< V, D, asd > &a)
Definition EigenADReal.h:50

Referenced by AdaptiveRayContainmentCheck().

◆ projectPointOntoPlane()

Point AdaptiveRayContainmentCheck::projectPointOntoPlane ( const Point &  point_to_project,
const Point &  plane_point,
const Point &  plane_normal 
) const
private

Orthogonally project point_to_project onto the plane defined by plane_point and unit normal plane_normal.

plane_normal is assumed to be a unit vector.

Definition at line 419 of file AdaptiveRayContainmentCheck.C.

422{
423 // Delegate to libMesh::Plane::closest_point, which returns the orthogonal
424 // projection of the point onto the plane. `plane_normal` is assumed to be a
425 // unit vector (closest_point does not normalize it).
426 return libMesh::Plane(plane_point, plane_normal).closest_point(point_to_project);
427}
virtual Point closest_point(const Point &p) const override

Referenced by buildObbKdtreeAndMaxProjectedDiagonal(), collectCandidateElementIDs(), and rayStartOutsideOBB().

◆ rayDirection()

const Point & AdaptiveRayContainmentCheck::rayDirection ( ) const
inline

The resolved ray direction actually used for shooting: the (normalized) user direction for a user-selected ray, or the PCA-selected direction for an auto ray.

Definition at line 53 of file AdaptiveRayContainmentCheck.h.

53{ return _ray_direction; }

◆ rayIntersectGeometry()

bool AdaptiveRayContainmentCheck::rayIntersectGeometry ( const Point &  ray_start,
const Point &  ray_end,
const SurfaceElement elem 
) const
private

Ray-element intersection (e.g., ray-line for 2D, ray-triangle for 3D)

Definition at line 250 of file AdaptiveRayContainmentCheck.C.

253{
254 LineSegment ray_segment(ray_start, ray_end);
255 return elem->intersect(ray_segment);
256}
The LineSegment class is used by the LineMaterialSamplerBase class and for some ray tracing stuff.
Definition LineSegment.h:31
virtual bool intersect(const LineSegment &line_segment) const =0
Check if the given line segment intersects this surface element.

Referenced by countCrossings().

◆ rayStartOutsideAABB()

Point AdaptiveRayContainmentCheck::rayStartOutsideAABB ( const Point &  point,
const Point &  unit_direction,
const bool  inverted 
) const
private

Ray start strictly outside the global AABB along unit_direction, for any direction.

The 8 AABB corners are projected onto the direction to find the box extent, and the start is placed just past the far side (a scale-aware padding), moving only the distance needed. inverted shoots from the opposite side. unit_direction must be normalized.

Definition at line 391 of file AdaptiveRayContainmentCheck.C.

394{
395 // Project the 8 AABB corners onto the direction to find the box's extent along it. The ray
396 // start is then placed just past the far side, so it is provably outside the box while moving
397 // only the distance needed (a full-diagonal displacement would make unnecessarily long rays).
398 const Point & lo = _bounds.min();
399 const Point & hi = _bounds.max();
400
401 Real min_projection = std::numeric_limits<Real>::max();
402 Real max_projection = std::numeric_limits<Real>::lowest();
403 for (const auto c : make_range(8u))
404 {
405 const Point corner(
406 (c & 1u) ? hi(0) : lo(0), (c & 2u) ? hi(1) : lo(1), (c & 4u) ? hi(2) : lo(2));
407 const Real projection = corner * unit_direction;
408 min_projection = std::min(min_projection, projection);
409 max_projection = std::max(max_projection, projection);
410 }
411
412 // Scale-aware padding so the start never lands exactly on the box boundary.
413 const Real padding = _eps_on_surface + 1e-2 * (max_projection - min_projection);
414 const Real target = inverted ? max_projection + padding : min_projection - padding;
415 return point + (target - point * unit_direction) * unit_direction;
416}

Referenced by sideness().

◆ rayStartOutsideOBB()

Point AdaptiveRayContainmentCheck::rayStartOutsideOBB ( const Point &  point,
const Point &  ray_direction,
const unsigned int  obb_axis,
const bool  inverted = false 
) const
private

Computes the starting point of an OBB-based ray (auto/PCA policy) for a given query point.

The point is projected onto the OBB face normal to ray_direction, selected by obb_axis, and displaced along that same direction so the ray originates outside the geometry.

Definition at line 332 of file AdaptiveRayContainmentCheck.C.

336{
337 mooseAssert(_build_obb,
338 "AdaptiveRayContainmentCheck::rayStartOutsideOBB: OBB-based ray start is only used "
339 "by the auto (PCA) policy.");
340 mooseAssert(obb_axis < static_cast<unsigned int>(_dim),
341 "AdaptiveRayContainmentCheck::rayStartOutsideOBB: invalid OBB axis index.");
342
343 const Real axis_length = _obb_bounds.getAxisLength(obb_axis);
344 const Real half_axis_length = axis_length / 2.0;
345
346 // The projection below lands the point exactly on the OBB face perpendicular to the ray
347 // (ray_direction == the OBB axis direction), so any positive outward step is provably
348 // outside the box. Use a scale-aware padding, consistent with rayStartOutsideAABB: an
349 // absolute floor (also guards a near-zero axis) plus a small fraction of the box extent
350 // to stay clear of floating-point noise at large coordinate scales.
351 const Real padding = _eps_on_surface + 1e-2 * axis_length;
352
353 Point projection_plane_corner;
354 Real direction_multiplier;
355
356 if (_obb_bounds.getProjectedLength(point, obb_axis) < half_axis_length)
357 {
358 projection_plane_corner =
360 direction_multiplier = inverted ? -1.0 : 1.0;
361 }
362 else
363 {
364 projection_plane_corner =
366 direction_multiplier = inverted ? 1.0 : -1.0;
367 }
368
369 const Point projected_point =
370 projectPointOntoPlane(point, projection_plane_corner, ray_direction);
371 return projected_point - padding * direction_multiplier * ray_direction;
372}
Real getAxisLength(unsigned int i) const
Real getProjectedLength(const Point &pt, unsigned int i) const
Get the length of the projection of a point onto axis i.

Referenced by sideness().

◆ sideness()

SurfaceGeometry::SurfaceSide AdaptiveRayContainmentCheck::sideness ( const Point &  p) const

Main function: Determine if a point is inside the geometry.

Definition at line 78 of file AdaptiveRayContainmentCheck.C.

79{
82
83 // Whether p sits on the surface is a property of p alone, so decide it once here rather than
84 // re-testing it on every ray cast below.
85 if (isOnSurface(p))
87
88 const std::array<Point, 2> ray_starts =
90 ? std::array<Point, 2>{rayStartOutsideOBB(p, _ray_direction, _dim - 1, false),
92 : std::array<Point, 2>{rayStartOutsideAABB(p, _ray_direction, false),
94
95 if (const auto side = sidenessFromRayPair(p, ray_starts))
96 return *side;
97
99 {
100 std::ostringstream oss;
101 oss << p;
102 mooseError("AdaptiveRayContainmentCheck: the user-selected ray_direction ",
104 " gives an ambiguous (grazing or tangent) intersection at point ",
105 oss.str(),
106 "; choose a different ray_direction or use the auto (pca_ray) method.");
107 }
108
109 // Defensive fallback. With the half-open crossing count, the two opposite primary rays always
110 // agree in parity for a well-formed closed surface, so this path is not reached for valid input;
111 // it is kept as a safety net should a future or degenerate case ever produce a parity
112 // disagreement. Probe the remaining PCA variance directions: if any probe ray escapes without
113 // crossing the surface, the point is outside. Otherwise the query is undecidable.
114 for (const auto obb_axis : make_range(static_cast<unsigned int>(_dim - 1)))
115 {
116 const Point fallback_direction = _obb_bounds.getAxisDirection(obb_axis);
117 const std::array<Point, 2> probe_starts = {
118 rayStartOutsideOBB(p, fallback_direction, obb_axis, false),
119 rayStartOutsideOBB(p, fallback_direction, obb_axis, true)};
120
121 for (const auto & probe_start : probe_starts)
122 if (countCrossings(probe_start, p, false) == 0)
124 }
125
126 std::ostringstream oss;
127 oss << p;
128 mooseError("AdaptiveRayContainmentCheck: No decision could be made for point " + oss.str());
129}
Point rayStartOutsideAABB(const Point &point, const Point &unit_direction, const bool inverted) const
Ray start strictly outside the global AABB along unit_direction, for any direction.
int countCrossings(const Point &ray_start, const Point &ray_end, const bool use_primary_direction=true) const
Count how many times the segment from ray_start to ray_end crosses the surface.
bool isOnSurface(const Point &p) const
True if p lies on the surface (within _eps_on_surface), i.e.
Point rayStartOutsideOBB(const Point &point, const Point &ray_direction, const unsigned int obb_axis, const bool inverted=false) const
Computes the starting point of an OBB-based ray (auto/PCA policy) for a given query point.
bool isOutsideBoundingBox(const Point &query_point) const
Check if point is outside global bounding box.
std::optional< SurfaceGeometry::SurfaceSide > sidenessFromRayPair(const Point &p, const std::array< Point, 2 > &ray_starts) const
Determine sideness from a pair of opposite rays.
Point getAxisDirection(unsigned int i) const
SurfaceSide
The side of a closed surface where a query point is located.
Definition SurfaceSide.h:21
@ ON
The point lies on the surface itself, within tolerance.
@ OUTSIDE
The point lies strictly in the exterior of the closed surface.

◆ sidenessFromRayPair()

std::optional< SurfaceGeometry::SurfaceSide > AdaptiveRayContainmentCheck::sidenessFromRayPair ( const Point &  p,
const std::array< Point, 2 > &  ray_starts 
) const
private

Determine sideness from a pair of opposite rays.

A conflicting parity returns std::nullopt so the caller can apply the selected ambiguity policy.

Definition at line 132 of file AdaptiveRayContainmentCheck.C.

134{
135 std::array<int, 2> counts = {0, 0};
136
137 // Shoot the two (opposite) rays and count intersections with the boundary elements. p has
138 // already been ruled on-surface by sideness(), so the count is always meaningful here.
139 for (const auto i : make_range(2))
140 {
141 counts[i] = countCrossings(ray_starts[i], p);
142
143 // A ray that never crosses the closed surface proves the point is outside.
144 if (counts[i] == 0)
146 }
147
148 // Consistent parity gives a definite decision; conflicting parity is undecided (nullopt) and
149 // left to the caller's policy.
150 if ((counts[0] % 2) == (counts[1] % 2))
151 return (counts[0] % 2 == 1) ? SurfaceGeometry::SurfaceSide::INSIDE
153 return std::nullopt;
154}
@ INSIDE
The point lies strictly in the interior of the closed surface.

Referenced by sideness().

Member Data Documentation

◆ _auto_ray_direction

bool AdaptiveRayContainmentCheck::_auto_ray_direction = false
private

Whether the ray direction is auto-selected via PCA (true) or user-selected (false).

Set once in the constructor from the SurfaceGeometry::RayDirectionOptions mode. Only an auto ray runs PCA and may have its direction chosen automatically.

Definition at line 112 of file AdaptiveRayContainmentCheck.h.

Referenced by AdaptiveRayContainmentCheck(), initializeRayDirection(), and sideness().

◆ _bd_elements

const std::vector<std::unique_ptr<SurfaceElement> >& AdaptiveRayContainmentCheck::_bd_elements
private

◆ _bounds

BoundingBox AdaptiveRayContainmentCheck::_bounds
private

The bounding box AABB.

Definition at line 107 of file AdaptiveRayContainmentCheck.h.

Referenced by initializeRayDirection(), isOutsideBoundingBox(), and rayStartOutsideAABB().

◆ _build_obb

bool AdaptiveRayContainmentCheck::_build_obb = false
private

When the ray direction is auto-selected (PCA) we build an Oriented Bounding Box (OBB); a user-selected ray uses only the global Axis-Aligned Bounding Box (AABB) instead.

Definition at line 116 of file AdaptiveRayContainmentCheck.h.

Referenced by buildObbKdtreeAndMaxProjectedDiagonal(), initializeRayDirection(), isOutsideBoundingBox(), and rayStartOutsideOBB().

◆ _centroid_nodal_points

Point AdaptiveRayContainmentCheck::_centroid_nodal_points
private

The centroid of the boundary elements' node points (prepare inside this class).

Definition at line 58 of file AdaptiveRayContainmentCheck.h.

Referenced by buildObbKdtreeAndMaxProjectedDiagonal(), and preparePCASVD().

◆ _centroids

const std::vector<Point>& AdaptiveRayContainmentCheck::_centroids
private

pass into the constructor for the surface element centroids

Definition at line 76 of file AdaptiveRayContainmentCheck.h.

Referenced by buildObbKdtreeAndMaxProjectedDiagonal().

◆ _comm

const libMesh::Parallel::Communicator* AdaptiveRayContainmentCheck::_comm = nullptr
private

Communicator used only for writing the debug OBB/ray mesh files.

Null when no debug output is requested.

Definition at line 101 of file AdaptiveRayContainmentCheck.h.

Referenced by buildObbKdtreeAndMaxProjectedDiagonal().

◆ _dim

int AdaptiveRayContainmentCheck::_dim = -1
private

◆ _eps_on_surface

Real AdaptiveRayContainmentCheck::_eps_on_surface
private

Epsilon value for checking if a point is on the surface of the geometry.

Definition at line 88 of file AdaptiveRayContainmentCheck.h.

Referenced by computeGlobalBoundingBox(), isOnSurface(), isOutsideBoundingBox(), rayStartOutsideAABB(), and rayStartOutsideOBB().

◆ _kd_tree

std::unique_ptr<KDTree> AdaptiveRayContainmentCheck::_kd_tree
private

The KDTree is constructed using the projected centroids of the elements in the boundary mesh.

(prepare inside this class)

Definition at line 67 of file AdaptiveRayContainmentCheck.h.

Referenced by buildObbKdtreeAndMaxProjectedDiagonal(), and collectCandidateElementIDs().

◆ _leaf_max_size

int AdaptiveRayContainmentCheck::_leaf_max_size
private

Configures KDTree leaf node size for performance tuning.

Definition at line 91 of file AdaptiveRayContainmentCheck.h.

Referenced by buildObbKdtreeAndMaxProjectedDiagonal().

◆ _max_projected_diag_length

Real AdaptiveRayContainmentCheck::_max_projected_diag_length
private

The maximum diagonal length of the projected bounding box from the boundary elements.

Definition at line 70 of file AdaptiveRayContainmentCheck.h.

Referenced by buildObbKdtreeAndMaxProjectedDiagonal(), and collectCandidateElementIDs().

◆ _max_variance_vector

Point AdaptiveRayContainmentCheck::_max_variance_vector
private

max variance vector

Definition at line 123 of file AdaptiveRayContainmentCheck.h.

Referenced by buildObbKdtreeAndMaxProjectedDiagonal(), and preparePCASVD().

◆ _min_variance_vector

Point AdaptiveRayContainmentCheck::_min_variance_vector
private

min variance vector (only used for 3D)

Definition at line 129 of file AdaptiveRayContainmentCheck.h.

Referenced by buildObbKdtreeAndMaxProjectedDiagonal(), initializeRayDirection(), and preparePCASVD().

◆ _num_elements

std::size_t AdaptiveRayContainmentCheck::_num_elements = 0
private

The number of elements in the boundary mesh.

Definition at line 82 of file AdaptiveRayContainmentCheck.h.

Referenced by AdaptiveRayContainmentCheck(), buildObbKdtreeAndMaxProjectedDiagonal(), and countFilteredCrossings().

◆ _obb_bounds

OrientedBoundingBox AdaptiveRayContainmentCheck::_obb_bounds
private

The oriented bounding box (OBB).

Definition at line 104 of file AdaptiveRayContainmentCheck.h.

Referenced by buildObbKdtreeAndMaxProjectedDiagonal(), isOutsideBoundingBox(), rayStartOutsideOBB(), and sideness().

◆ _obb_file_name

FileName AdaptiveRayContainmentCheck::_obb_file_name
private

The file name for the OBB.

Definition at line 94 of file AdaptiveRayContainmentCheck.h.

Referenced by buildObbKdtreeAndMaxProjectedDiagonal().

◆ _plane_origin

Point AdaptiveRayContainmentCheck::_plane_origin = Point(0.0, 0.0, 0.0)
private

The origin of the plane used to ensure that every projected point is correctly aligned and lies on the same plane.

This is for fast k-d tree searching to select the candidate elements to check the intersection.

Definition at line 120 of file AdaptiveRayContainmentCheck.h.

Referenced by buildObbKdtreeAndMaxProjectedDiagonal(), and collectCandidateElementIDs().

◆ _projected_centroids

std::vector<Point> AdaptiveRayContainmentCheck::_projected_centroids
private

Projected centroids of the elements in the boundary mesh.

(prepare inside this class) This is used to construct the KDTree in this struct for fast searching.

Definition at line 63 of file AdaptiveRayContainmentCheck.h.

Referenced by buildObbKdtreeAndMaxProjectedDiagonal().

◆ _ray_direction

Point AdaptiveRayContainmentCheck::_ray_direction
private

◆ _ray_file_name

FileName AdaptiveRayContainmentCheck::_ray_file_name
private

The file name for the ray.

Definition at line 97 of file AdaptiveRayContainmentCheck.h.

Referenced by buildObbKdtreeAndMaxProjectedDiagonal().

◆ _second_variance_vector

Point AdaptiveRayContainmentCheck::_second_variance_vector
private

second max variance vector

Definition at line 126 of file AdaptiveRayContainmentCheck.h.

Referenced by buildObbKdtreeAndMaxProjectedDiagonal(), initializeRayDirection(), and preparePCASVD().


The documentation for this class was generated from the following files: