https://mooseframework.inl.gov
Loading...
Searching...
No Matches
SBMInterfaceManager.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
10#include "Ball.h"
11#include "SBMSurfaceDistance.h"
12
13#include "libmesh/mesh_tools.h"
14
15#include <algorithm>
16#include <cmath>
17#include <iterator>
18
19registerMooseObject("ShiftedBoundaryMethodApp", SBMInterfaceManager);
20
23{
25 params.renameParam(
26 "complete_boundary_mesh",
27 "complete_interface_mesh",
28 "The saved mesh containing the complete boundary of each interface subdomain.");
29 params.addRangeCheckedParam<unsigned int>(
30 "leaf_max_size", 10, "leaf_max_size > 0", "Maximum number of points in a k-d tree leaf.");
31 params.addRangeCheckedParam<Real>(
32 "tolerance",
34 "tolerance >= 0",
35 "Relative distance tolerance for matching faces, scaled by the surface mesh size.");
36 params.addRangeCheckedParam<Real>("normal_tolerance",
37 1e-3,
38 "normal_tolerance >= 0",
39 "Tolerance for comparing unit face normals.");
41 "Detects shared interfaces between subdomains in a single surface mesh that contains the "
42 "complete boundary of each subdomain, and provides distance and normal queries for each "
43 "interface.");
44 return params;
45}
46
49 _leaf_max_size(getParam<unsigned int>("leaf_max_size")),
50 _relative_distance_tolerance(getParam<Real>("tolerance")),
51 _normal_tolerance(getParam<Real>("normal_tolerance"))
52{
53}
54
55void
63
64void
66{
67 _interfaces.clear();
68 const auto & sets = getSurfaceElementSetsBySubdomain();
69 if (sets.size() < 2)
70 mooseError("SBMInterfaceManager requires surfaces for at least two subdomains.");
71
72 for (auto first_it = sets.begin(); first_it != sets.end(); ++first_it)
73 for (auto second_it = std::next(first_it); second_it != sets.end(); ++second_it)
74 {
75 const auto first_id = first_it->first;
76 const auto second_id = second_it->first;
77 const auto key = std::minmax(first_id, second_id);
78 const auto & source_set = key.first == first_id ? first_it->second : second_it->second;
79 const auto & target_set = key.first == first_id ? second_it->second : first_it->second;
80
81 Real target_max_radius = 0.0;
82 for (const auto & element : target_set.elements())
83 target_max_radius = std::max(target_max_radius, element->computeBoundingBall().radius());
84
85 KDTree target_tree(target_set.centroids(), _leaf_max_size);
86 auto & interface = _interfaces[{key.first, key.second}];
87 std::vector<nanoflann::ResultItem<std::size_t, Real>> candidates;
88
89 for (const auto & source : source_set.elements())
90 {
91 const Point centroid = source->elem().vertex_average();
92 const Real search_radius =
93 source->computeBoundingBall().radius() + target_max_radius + _distance_tolerance;
94 candidates.clear();
95 target_tree.radiusSearch(centroid, search_radius, candidates);
96
97 const auto matched = std::find_if(
98 candidates.begin(),
99 candidates.end(),
100 [&](const auto & candidate)
101 {
102 const auto & target = *target_set.elements()[candidate.first];
103 if (std::abs(std::abs(source->normal() * target.normal()) - 1.0) > _normal_tolerance)
104 return false;
105
106 const auto all_vertices_on = [&](const Elem & element, const SurfaceElement & surface)
107 {
108 for (const auto & node : element.node_ref_range())
109 if (SBMUtils::distanceFrom(surface, node).norm() > _distance_tolerance)
110 return false;
111 return true;
112 };
113 return all_vertices_on(source->elem(), target) &&
114 all_vertices_on(target.elem(), *source);
115 });
116
117 if (matched != candidates.end())
118 {
119 interface.elements.push_back(source.get());
120 interface.centroids.push_back(centroid);
121 interface.max_element_radius =
122 std::max(interface.max_element_radius, source->computeBoundingBall().radius());
123 }
124 }
125
126 if (interface.elements.empty())
127 _interfaces.erase({key.first, key.second});
128 else
129 interface.kd_tree = std::make_unique<KDTree>(interface.centroids, _leaf_max_size);
130 }
131}
132
133bool
135{
136 const auto pair = std::minmax(first, second);
137 return _interfaces.count({pair.first, pair.second});
138}
139
142 SubdomainID second,
143 const Point & point) const
144{
145 const auto ordered = std::minmax(first, second);
146 const auto interface_it = _interfaces.find({ordered.first, ordered.second});
147 if (interface_it == _interfaces.end())
148 mooseError("SBMInterfaceManager: interface (", first, ", ", second, ") was not detected.");
149
150 const auto & interface = interface_it->second;
151 std::vector<std::size_t> indices;
152 interface.kd_tree->neighborSearch(point, 1, indices);
153 std::size_t closest = indices.front();
154 Point distance = SBMUtils::distanceFrom(*interface.elements[closest], point);
155
156 std::vector<nanoflann::ResultItem<std::size_t, Real>> candidates;
157 interface.kd_tree->radiusSearch(
158 point, distance.norm() + interface.max_element_radius, candidates);
159 for (const auto & candidate : candidates)
160 {
161 const Point candidate_distance =
162 SBMUtils::distanceFrom(*interface.elements[candidate.first], point);
163 if (candidate_distance.norm() < distance.norm())
164 {
165 closest = candidate.first;
166 distance = candidate_distance;
167 }
168 }
169
170 Point normal = interface.elements[closest]->normal();
171 if (first > second)
172 normal *= -1.0;
173 return {distance, normal};
174}
registerMooseObject("ShiftedBoundaryMethodApp", SBMInterfaceManager)
void ErrorVector unsigned int
MeshBase & mesh() const
void renameParam(const std::string &old_name, const std::string &new_name, const std::string &new_docstring)
void addClassDescription(const std::string &doc_string)
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
void radiusSearch(const Point &query_point, Real radius, std::vector< nanoflann::ResultItem< std::size_t, Real > > &indices_dist)
void mooseError(Args &&... args) const
Detects shared interfaces between subdomains in a single surface mesh containing the complete boundar...
static InputParameters validParams()
const Real _relative_distance_tolerance
Relative tolerance for face matching, scaled by mesh size.
InterfaceQueryResult queryInterface(SubdomainID first, SubdomainID second, const Point &point) const
Return distance and normal data for an interface. The normal points from first to second.
Real _distance_tolerance
Absolute distance tolerance for face matching.
SBMInterfaceManager(const InputParameters &parameters)
bool hasInterface(SubdomainID first, SubdomainID second) const
Whether an interface was detected between two subdomains.
std::map< std::pair< SubdomainID, SubdomainID >, InterfaceData > _interfaces
Map of detected interfaces keyed by sorted subdomain ID pairs.
virtual void initialSetup() override
const unsigned int _leaf_max_size
Maximum number of points in a k-d tree leaf node.
BoundaryMeshBuilder specialization that groups the surface elements by subdomain, building one Surfac...
const std::unordered_map< subdomain_id_type, SurfaceElementSet > & getSurfaceElementSetsBySubdomain() const
Per-subdomain SurfaceElementSets. Valid after initialSetup().
libMesh::Point distanceFrom(const SurfaceElement &surface_elem, const libMesh::Point &pt)
Returns the vector from pt to the nearest point on the surface element: the normal projection if it f...
static constexpr Real TOLERANCE
Result of querying distance and normal to an interface.
Real distance(const Point &p)