https://mooseframework.inl.gov
Loading...
Searching...
No Matches
SBMUtils.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 "SBMUtils.h"
11#include "Function.h"
12#include "MooseUtils.h"
13#include "FunctionInterface.h"
14#include "MooseParsedFunction.h"
17#include "libmesh/fe.h"
18#include "libmesh/quadrature_gauss.h"
19
20#include <limits>
21
22namespace SBMUtils
23{
24
25Real
26activeElementFraction(const Elem & elem,
27 Order qrule_order,
28 const std::function<bool(const libMesh::Point &)> & is_active)
29{
30 const FEType fe_type(elem.default_order(), LAGRANGE);
31 auto fe = FEBase::build(elem.dim(), fe_type);
32 QGauss qrule(elem.dim(), qrule_order);
33 const auto & q_points = fe->get_xyz();
34 const auto & JxW = fe->get_JxW();
35 fe->attach_quadrature_rule(&qrule);
36 fe->reinit(&elem);
37
38 Real active_measure = 0.0;
39 Real total_measure = 0.0;
40 for (const auto i : index_range(q_points))
41 {
42 if (is_active(q_points[i]))
43 active_measure += JxW[i];
44 total_measure += JxW[i];
45 }
46
47 return active_measure / total_measure;
48}
49
50ElementDomainOccupancy
51elementDomainOccupancy(const Elem & elem,
52 const Order qrule_order,
53 const std::function<bool(const libMesh::Point &)> & is_in_domain)
54{
55 const auto is_outside_domain = [&is_in_domain](const Point & point)
56 { return !is_in_domain(point); };
57 return elementDomainOccupancy(elem, qrule_order, is_in_domain, is_outside_domain);
58}
59
60ElementDomainOccupancy
61elementDomainOccupancy(const Elem & elem,
62 const Order qrule_order,
63 const std::function<bool(const libMesh::Point &)> & is_in_domain,
64 const std::function<bool(const libMesh::Point &)> & is_outside_domain)
65{
66 bool all_nodes_in_domain = true;
67 bool all_nodes_outside_domain = true;
68 for (const auto node : make_range(elem.n_nodes()))
69 {
70 const auto & point = elem.point(node);
71 all_nodes_in_domain = all_nodes_in_domain && is_in_domain(point);
72 all_nodes_outside_domain = all_nodes_outside_domain && is_outside_domain(point);
73 }
74
75 return {all_nodes_in_domain,
76 all_nodes_outside_domain,
77 activeElementFraction(elem, qrule_order, is_in_domain)};
78}
79
80bool
81isInactive(const Real active_fraction, const Real lambda)
82{
83 if (MooseUtils::absoluteFuzzyEqual(lambda, 0))
84 return true;
85 if (MooseUtils::absoluteFuzzyEqual(lambda, 1))
86 return false;
87
88 return MooseUtils::absoluteFuzzyGreaterThan(1.0 - active_fraction, lambda);
89}
90
91std::optional<SubdomainID>
92selectSubdomainFromOccupancies(const std::vector<SubdomainOccupancy> & candidate_occupancies,
93 const InterceptedSubdomainPolicy & intercepted_policy,
94 const Real lambda)
95{
96 std::optional<SubdomainID> fully_inside_subdomain;
97 std::optional<SubdomainID> best_partial_subdomain;
98 Real max_fraction = 0.0;
99
100 for (const auto & candidate : candidate_occupancies)
101 {
102 const auto & occupancy = candidate.occupancy;
103 if (occupancy.all_nodes_in_domain && occupancy.fraction_of_element_in_domain == 1.0)
104 {
105 if (!fully_inside_subdomain || candidate.subdomain_id < *fully_inside_subdomain)
106 fully_inside_subdomain = candidate.subdomain_id;
107 }
108 else if ((!occupancy.all_nodes_outside_domain ||
109 occupancy.fraction_of_element_in_domain > 0.0) &&
110 occupancy.fraction_of_element_in_domain >= max_fraction)
111 max_fraction = occupancy.fraction_of_element_in_domain;
112 }
113
114 if (fully_inside_subdomain)
115 return fully_inside_subdomain;
116
117 for (const auto & candidate : candidate_occupancies)
118 if ((!candidate.occupancy.all_nodes_outside_domain ||
119 candidate.occupancy.fraction_of_element_in_domain > 0.0) &&
120 MooseUtils::absoluteFuzzyEqual(candidate.occupancy.fraction_of_element_in_domain,
121 max_fraction) &&
122 (!best_partial_subdomain || candidate.subdomain_id < *best_partial_subdomain))
123 best_partial_subdomain = candidate.subdomain_id;
124
125 if (!best_partial_subdomain)
126 return std::nullopt;
127
128 if (intercepted_policy.mark_intercepted)
129 return intercepted_policy.subdomain_id;
130
131 if (isInactive(max_fraction, lambda))
132 return std::nullopt;
133
134 return best_partial_subdomain;
135}
136
139 const ClassificationSubdomains & subdomain_id_settings,
140 const bool mark_intercepted,
141 const Real lambda)
142{
143 // Same-side nodes do not rule out a surface crossing the element or enclosing a region
144 // within it. Quadrature sampling detects most such cases, but may miss very small regions.
145 // Exact endpoint comparisons are intentional: activeElementFraction returns exactly zero
146 // or one when no or all quadrature points are in the retained domain, respectively.
147 if (occupancy.all_nodes_in_domain && occupancy.fraction_of_element_in_domain == 1.0)
148 return subdomain_id_settings.inside;
149
150 if (occupancy.all_nodes_outside_domain && occupancy.fraction_of_element_in_domain == 0.0)
151 return subdomain_id_settings.outside;
152
153 if (mark_intercepted)
154 return subdomain_id_settings.intercepted;
155
156 return isInactive(occupancy.fraction_of_element_in_domain, lambda) ? subdomain_id_settings.outside
157 : subdomain_id_settings.inside;
158}
159
160bool
161checkWatertightnessFromRawElems(const std::vector<const Elem *> & bd_elements)
162{
163 for (const auto * el : bd_elements)
164 for (unsigned int s = 0; s < el->n_sides(); ++s)
165 if (!el->neighbor_ptr(s))
166 return false;
167
168 return true;
169}
170
171std::vector<const Function *>
172buildDistanceFunctions(const std::vector<FunctionName> & function_names,
173 const FunctionInterface & function_provider)
174{
175 std::vector<const Function *> funcs;
176 funcs.reserve(function_names.size());
177
178 for (const auto & name : function_names)
179 {
180 const Function * func = &function_provider.getFunctionByName(name);
181 if (!dynamic_cast<const MooseParsedFunction *>(func) &&
182 !dynamic_cast<const UnsignedDistanceToSurfaceMesh *>(func) &&
183 !dynamic_cast<const SignedDistanceToSurfaceMesh *>(func))
184 {
185 mooseError("SBM distance helpers only support ParsedFunction, "
186 "UnsignedDistanceToSurfaceMesh, or SignedDistanceToSurfaceMesh types. Offending "
187 "function: ",
188 name);
189 }
190 funcs.emplace_back(func);
191 }
192
193 return funcs;
194}
195
196RealVectorValue
197distanceVectorFromFunction(const Function * func, const libMesh::Point & pt, Real t)
198{
199 mooseAssert(dynamic_cast<const MooseParsedFunction *>(func) ||
200 dynamic_cast<const UnsignedDistanceToSurfaceMesh *>(func) ||
201 dynamic_cast<const SignedDistanceToSurfaceMesh *>(func),
202 "Function was not a valid distance strategy, the only "
203 "supported types are ParsedFunction, UnsignedDistanceToSurfaceMesh, or "
204 "SignedDistanceToSurfaceMesh.");
205
206 const Real phi = func->value(t, pt);
207 const RealVectorValue grad_phi = func->gradient(t, pt);
208 const Real grad_norm = grad_phi.norm();
209
210 if (grad_norm <= libMesh::TOLERANCE)
211 return RealVectorValue(0.0, 0.0, 0.0);
212
213 return -(phi / grad_norm) * grad_phi;
214}
215
216RealVectorValue
217trueNormalFromFunction(const Function * func, const libMesh::Point & pt, Real t)
218{
219 if (const auto * parsed = dynamic_cast<const MooseParsedFunction *>(func))
220 {
221 const auto proj_pt = pt + distanceVectorFromFunction(func, pt, t);
222 const RealVectorValue grad_phi = parsed->gradient(t, proj_pt);
223 const Real grad_norm = grad_phi.norm();
224 if (grad_norm <= libMesh::TOLERANCE)
225 return RealVectorValue(0.0, 0.0, 0.0);
226 return grad_phi / grad_norm;
227 }
228 else
229 {
230 const auto * mesh_func = dynamic_cast<const UnsignedDistanceToSurfaceMesh *>(func);
231 if (!mesh_func)
232 mesh_func = dynamic_cast<const SignedDistanceToSurfaceMesh *>(func);
233
234 mooseAssert(mesh_func, "Function was not a valid distance strategy");
235 return mesh_func->surfaceNormal(pt);
236 }
237}
238
239RealVectorValue
240closestDistanceVector(const std::vector<const Function *> & funcs,
241 const libMesh::Point & pt,
242 Real t)
243{
244 Real min_dist = std::numeric_limits<Real>::max();
245 RealVectorValue closest_dist_vec;
246
247 for (const auto & func : funcs)
248 {
249 const auto dist_vec = distanceVectorFromFunction(func, pt, t);
250 const auto dist = dist_vec.norm();
251 if (dist < min_dist)
252 {
253 min_dist = dist;
254 closest_dist_vec = dist_vec;
255 }
256 }
257
258 return closest_dist_vec;
259}
260
261RealVectorValue
262closestTrueNormalVector(const std::vector<const Function *> & funcs,
263 const libMesh::Point & pt,
264 Real t)
265{
266 Real min_dist = std::numeric_limits<Real>::max();
267 RealVectorValue closest_normal_vec;
268
269 for (const auto & func : funcs)
270 {
271 const auto dist_vec = distanceVectorFromFunction(func, pt, t);
272 const auto dist = dist_vec.norm();
273 if (dist < min_dist)
274 {
275 min_dist = dist;
276 closest_normal_vec = trueNormalFromFunction(func, pt, t);
277 }
278 }
279
280 return closest_normal_vec;
281}
282
283Real
284unionSignedDistance(const std::vector<const Function *> & funcs, Real t, const Point & p)
285{
286 // Ensure all distance functions are valid signed distance strategies
287 for (const auto * func : funcs)
288 {
289 if (!dynamic_cast<const MooseParsedFunction *>(func) &&
290 !dynamic_cast<const SignedDistanceToSurfaceMesh *>(func))
291 mooseError("Signed distance requested but function was not a valid signed distance strategy. "
292 "Valid types are MooseParsedFunction or SignedDistanceToSurfaceMesh. "
293 "Offending function: ",
294 func->name());
295 }
296
297 mooseAssert(!funcs.empty(), "unionSignedDistance requires at least one function.");
298
299 // Union signed distance: min of all signed distances
300 Real min_value = funcs[0]->value(t, p);
301 for (const auto i : make_range(std::size_t(1), funcs.size()))
302 {
303 const Real val = funcs[i]->value(t, p);
304 if (val < min_value)
305 min_value = val;
306 }
307
308 return min_value;
309}
310
311} // namespace SBMUtils
subdomain_id_type SubdomainID
const Real p
void mooseError(Args &&... args)
const std::string name
Definition Setup.h:21
const Function & getFunctionByName(const FunctionName &name) const
virtual RealGradient gradient(Real t, const Point &p) const
virtual Real value(Real t, const Point &p) const
Computes the signed distance to a surface mesh using k-d tree nearest neighbor lookup from SBMSurface...
Computes the unsigned distance to a surface mesh using geometry supplied by SBMSurfaceMeshBuilder or ...
std::optional< SubdomainID > selectSubdomainFromOccupancies(const std::vector< SubdomainOccupancy > &candidate_occupancies, const InterceptedSubdomainPolicy &intercepted_policy, Real lambda)
Select a subdomain based on how multiple candidate domains occupy an element.
Definition SBMUtils.C:92
ElementDomainOccupancy elementDomainOccupancy(const Elem &elem, Order qrule_order, const std::function< bool(const libMesh::Point &)> &is_in_domain)
Test an element against the domain defined by is_in_domain.
Definition SBMUtils.C:51
Real activeElementFraction(const Elem &elem, Order qrule_order, const std::function< bool(const libMesh::Point &)> &is_active)
Compute the fraction of an element's quadrature-weighted measure satisfying a predicate.
Definition SBMUtils.C:26
Real unionSignedDistance(const std::vector< const Function * > &funcs, Real t, const Point &p)
Computes the union signed distance by taking the minimum of all signed distance functions.
Definition SBMUtils.C:284
RealVectorValue closestTrueNormalVector(const std::vector< const Function * > &funcs, const libMesh::Point &pt, Real t)
Scan all distance functions and return the corresponding normal vector.
Definition SBMUtils.C:262
bool checkWatertightnessFromRawElems(const std::vector< const Elem * > &bd_elements)
Definition SBMUtils.C:161
SubdomainID classifySubdomainFromOccupancy(const ElementDomainOccupancy &occupancy, const ClassificationSubdomains &subdomain_id_settings, bool mark_intercepted, Real lambda)
Classify an element from its retained-domain occupancy measurements.
Definition SBMUtils.C:138
bool isInactive(Real active_fraction, Real lambda)
Return whether a partial element is inactive, i.e.
Definition SBMUtils.C:81
std::vector< const Function * > buildDistanceFunctions(const std::vector< FunctionName > &function_names, const FunctionInterface &function_provider)
Build a list of distance functions based on names specified in input.
Definition SBMUtils.C:172
RealVectorValue distanceVectorFromFunction(const Function *func, const libMesh::Point &pt, Real t)
Compute the distance vector induced by a distance function.
Definition SBMUtils.C:197
RealVectorValue trueNormalFromFunction(const Function *func, const libMesh::Point &pt, Real t)
Compute the true boundary surface normal at the point on the boundary closest to pt.
Definition SBMUtils.C:217
RealVectorValue closestDistanceVector(const std::vector< const Function * > &funcs, const libMesh::Point &pt, Real t)
Scan all distance functions and return the closest distance vector.
Definition SBMUtils.C:240
static constexpr Real TOLERANCE
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
The subdomain IDs a (possibly partial) element can be labeled with.
Definition SBMUtils.h:102
Results of testing an element against a domain.
Definition SBMUtils.h:45
bool all_nodes_in_domain
Whether all element nodes lie in the retained domain.
Definition SBMUtils.h:47
bool all_nodes_outside_domain
Whether all element nodes lie outside the retained domain.
Definition SBMUtils.h:49
Real fraction_of_element_in_domain
Quadrature-based estimate of the fraction of this element inside the domain.
Definition SBMUtils.h:51
Policy for assigning partial elements to a dedicated intercepted subdomain.
Definition SBMUtils.h:73