17#include "libmesh/fe.h"
18#include "libmesh/quadrature_gauss.h"
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);
38 Real active_measure = 0.0;
39 Real total_measure = 0.0;
40 for (
const auto i : index_range(q_points))
42 if (is_active(q_points[i]))
43 active_measure += JxW[i];
44 total_measure += JxW[i];
47 return active_measure / total_measure;
52 const Order qrule_order,
55 const auto is_outside_domain = [&is_in_domain](
const Point & point)
56 {
return !is_in_domain(point); };
62 const Order qrule_order,
64 const std::function<
bool(
const libMesh::Point &)> & is_outside_domain)
66 bool all_nodes_in_domain =
true;
67 bool all_nodes_outside_domain =
true;
68 for (
const auto node : make_range(elem.n_nodes()))
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);
75 return {all_nodes_in_domain,
76 all_nodes_outside_domain,
83 if (MooseUtils::absoluteFuzzyEqual(lambda, 0))
85 if (MooseUtils::absoluteFuzzyEqual(lambda, 1))
88 return MooseUtils::absoluteFuzzyGreaterThan(1.0 - active_fraction, lambda);
91std::optional<SubdomainID>
96 std::optional<SubdomainID> fully_inside_subdomain;
97 std::optional<SubdomainID> best_partial_subdomain;
98 Real max_fraction = 0.0;
100 for (
const auto & candidate : candidate_occupancies)
102 const auto & occupancy = candidate.occupancy;
103 if (occupancy.all_nodes_in_domain && occupancy.fraction_of_element_in_domain == 1.0)
105 if (!fully_inside_subdomain || candidate.subdomain_id < *fully_inside_subdomain)
106 fully_inside_subdomain = candidate.subdomain_id;
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;
114 if (fully_inside_subdomain)
115 return fully_inside_subdomain;
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,
122 (!best_partial_subdomain || candidate.subdomain_id < *best_partial_subdomain))
123 best_partial_subdomain = candidate.subdomain_id;
125 if (!best_partial_subdomain)
134 return best_partial_subdomain;
140 const bool mark_intercepted,
148 return subdomain_id_settings.
inside;
151 return subdomain_id_settings.
outside;
153 if (mark_intercepted)
157 : subdomain_id_settings.
inside;
163 for (
const auto * el : bd_elements)
164 for (
unsigned int s = 0; s < el->n_sides(); ++s)
165 if (!el->neighbor_ptr(s))
171std::vector<const Function *>
175 std::vector<const Function *> funcs;
176 funcs.reserve(function_names.size());
178 for (
const auto &
name : function_names)
185 mooseError(
"SBM distance helpers only support ParsedFunction, "
186 "UnsignedDistanceToSurfaceMesh, or SignedDistanceToSurfaceMesh types. Offending "
190 funcs.emplace_back(func);
202 "Function was not a valid distance strategy, the only "
203 "supported types are ParsedFunction, UnsignedDistanceToSurfaceMesh, or "
204 "SignedDistanceToSurfaceMesh.");
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();
211 return RealVectorValue(0.0, 0.0, 0.0);
213 return -(phi / grad_norm) * grad_phi;
222 const RealVectorValue grad_phi = parsed->gradient(t, proj_pt);
223 const Real grad_norm = grad_phi.norm();
225 return RealVectorValue(0.0, 0.0, 0.0);
226 return grad_phi / grad_norm;
234 mooseAssert(mesh_func,
"Function was not a valid distance strategy");
235 return mesh_func->surfaceNormal(pt);
244 Real min_dist = std::numeric_limits<Real>::max();
245 RealVectorValue closest_dist_vec;
247 for (
const auto & func : funcs)
250 const auto dist = dist_vec.norm();
254 closest_dist_vec = dist_vec;
258 return closest_dist_vec;
266 Real min_dist = std::numeric_limits<Real>::max();
267 RealVectorValue closest_normal_vec;
269 for (
const auto & func : funcs)
272 const auto dist = dist_vec.norm();
280 return closest_normal_vec;
287 for (
const auto * func : funcs)
291 mooseError(
"Signed distance requested but function was not a valid signed distance strategy. "
292 "Valid types are MooseParsedFunction or SignedDistanceToSurfaceMesh. "
293 "Offending function: ",
297 mooseAssert(!funcs.empty(),
"unionSignedDistance requires at least one function.");
300 Real min_value = funcs[0]->value(t,
p);
301 for (
const auto i : make_range(std::size_t(1), funcs.size()))
303 const Real val = funcs[i]->value(t,
p);
subdomain_id_type SubdomainID
void mooseError(Args &&... args)
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.
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.
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.
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.
RealVectorValue closestTrueNormalVector(const std::vector< const Function * > &funcs, const libMesh::Point &pt, Real t)
Scan all distance functions and return the corresponding normal vector.
bool checkWatertightnessFromRawElems(const std::vector< const Elem * > &bd_elements)
SubdomainID classifySubdomainFromOccupancy(const ElementDomainOccupancy &occupancy, const ClassificationSubdomains &subdomain_id_settings, bool mark_intercepted, Real lambda)
Classify an element from its retained-domain occupancy measurements.
bool isInactive(Real active_fraction, Real lambda)
Return whether a partial element is inactive, i.e.
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.
RealVectorValue distanceVectorFromFunction(const Function *func, const libMesh::Point &pt, Real t)
Compute the distance vector induced by a distance function.
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.
RealVectorValue closestDistanceVector(const std::vector< const Function * > &funcs, const libMesh::Point &pt, Real t)
Scan all distance functions and return the closest distance vector.
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.
Results of testing an element against a domain.
bool all_nodes_in_domain
Whether all element nodes lie in the retained domain.
bool all_nodes_outside_domain
Whether all element nodes lie outside the retained domain.
Real fraction_of_element_in_domain
Quadrature-based estimate of the fraction of this element inside the domain.
Policy for assigning partial elements to a dedicated intercepted subdomain.