https://mooseframework.inl.gov
Loading...
Searching...
No Matches
SubdomainGrainIDGenerator.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://www.mooseframework.org
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
11#include "SBMUtils.h"
12
14
17{
19
20 params.addRequiredParam<MeshGeneratorName>("boundary_mesh",
21 "The boundary mesh to use for identifying grain IDs");
22
23 // --- Point-In-Polyhedron checks ---
24
25 params.addParam<Point>(
26 "ray_direction", Point(0, 0, 0), "The direction of the ray for in-out testing.");
27
28 params.addParam<Real>("eps",
30 "Tolerance value used for intersection or surface proximity checks. "
31 "This parameter determines whether a point is considered on the geometry "
32 "or on the in/out sides of the geometry.");
33
34 params.addRangeCheckedParam<unsigned int>(
35 "leaf_max_size",
36 10,
37 "leaf_max_size > 0",
38 "Maximum number of boundary-element centroids per leaf node in the k-d tree "
39 "used to accelerate ray-casting point-in-grain checks. Tuning this parameter "
40 "only affects search performance, not the assigned subdomain IDs.");
41
43 "Based on the boundary_mesh, which contains distinct grain boundaries as separate "
44 "watertight regions, this generator assigns grain IDs (subdomain IDs) to the volume mesh. "
45 "Even if the mesh does not perfectly align with the grain boundaries, each element is "
46 "assigned the selected grain ID, or retains its input subdomain ID when no grain is "
47 "selected.");
48
49 return params;
50}
51
53 : SBMSubdomainGeneratorBase(parameters),
54 _boundary_mesh(getMesh("boundary_mesh")),
55 _ray_direction(getParam<Point>("ray_direction")),
56 _eps(getParam<Real>("eps")),
57 _leaf_max_size(getParam<unsigned int>("leaf_max_size"))
58{
59}
60
61std::unique_ptr<libMesh::MeshBase>
63{
64 // Take ownership of the input mesh (already cloned by getMesh()).
65 std::unique_ptr<libMesh::MeshBase> mesh = std::move(_input);
66
67 std::set<SubdomainID> boundary_subdomain_ids;
68 // Bind a const reference so that we call the non-deprecated const subdomain_name() overload.
69 const libMesh::MeshBase & boundary_mesh = *_boundary_mesh;
70 boundary_mesh.subdomain_ids(boundary_subdomain_ids);
71 for (const auto subdomain_id : boundary_subdomain_ids)
72 mesh->set_subdomain_name(subdomain_id, boundary_mesh.subdomain_name(subdomain_id));
73
74 // (a) read the boundary mesh to be our own data structure
76
77 // (b) build the point-containment checker for each subdomain
79
80 std::vector<const SubdomainCheckerEntry *> candidate_entries;
81 candidate_entries.reserve(_checker_entries.size());
82
83 std::vector<SBMUtils::SubdomainOccupancy> candidate_occupancies;
84 candidate_occupancies.reserve(_checker_entries.size());
85
86 for (const auto & elem : mesh->active_element_ptr_range() /*gen only run rank = 0*/)
87 {
88 const auto elem_bbox = elem->loose_bounding_box();
89 candidate_entries.clear();
90 candidate_occupancies.clear();
91
92 for (const auto & entry : _checker_entries)
93 if (boundingBoxesIntersect(elem_bbox, entry.bbox, _eps))
94 candidate_entries.push_back(&entry);
95
96 if (candidate_entries.empty())
97 for (const auto & entry : _checker_entries)
98 candidate_entries.push_back(&entry);
99
100 for (const auto * entry : candidate_entries)
101 {
102 const auto * const checker = entry->checker;
103 const auto is_in_domain = [checker](const Point & point)
104 { return checker->sideness(point) != SurfaceGeometry::SurfaceSide::OUTSIDE; };
105 candidate_occupancies.push_back(
106 {entry->subdomain_id,
107 SBMUtils::elementDomainOccupancy(*elem, _qrule_order, is_in_domain)});
108 }
109
110 const auto subdomain = SBMUtils::selectSubdomainFromOccupancies(
111 candidate_occupancies, _intercepted_subdomain_policy, _lambda);
112 if (subdomain)
113 elem->subdomain_id() = *subdomain;
114 }
115
116 // Signal that the mesh has been modified and needs preparation.
117 mesh->unset_is_prepared();
118 return mesh;
119}
120
121void
123{
124 struct SubdomainBuildBucket
125 {
126 SubdomainID subdomain_id;
127 std::size_t count = 0;
128 BoundingBox bbox;
129 bool has_bbox = false;
130 std::vector<Point> centroids;
131 std::vector<std::unique_ptr<SurfaceElement>> boundary_elements;
132 };
133
134 _boundary_owned = std::move(_boundary_mesh);
137 _bboxes_by_subdomain.clear();
138
139 libMesh::MeshBase * boundary_mesh = _boundary_owned.get();
140 const auto estimated_subdomains = boundary_mesh->n_subdomains();
141
142 std::unordered_map<SubdomainID, std::size_t> bucket_index_by_subdomain;
143 bucket_index_by_subdomain.reserve(estimated_subdomains);
144
145 std::vector<SubdomainBuildBucket> buckets;
146 buckets.reserve(estimated_subdomains);
147
148 auto get_or_create_bucket = [&](const SubdomainID sid) -> SubdomainBuildBucket &
149 {
150 const auto [it, inserted] = bucket_index_by_subdomain.emplace(sid, buckets.size());
151 if (inserted)
152 buckets.push_back({sid, 0, {}, false, {}, {}});
153 return buckets[it->second];
154 };
155
156 for (const auto & elem : boundary_mesh->active_element_ptr_range())
157 {
158 const auto sid = elem->subdomain_id();
159 auto & bucket = get_or_create_bucket(sid);
160 ++bucket.count;
161
162 const auto elem_bbox = elem->loose_bounding_box();
163 if (!bucket.has_bbox)
164 {
165 bucket.bbox = elem_bbox;
166 bucket.has_bbox = true;
167 }
168 else
169 bucket.bbox.union_with(elem_bbox);
170 }
171
172 _centroids_by_subdomain.reserve(buckets.size());
173 _boundary_elements_by_subdomain.reserve(buckets.size());
174 _bboxes_by_subdomain.reserve(buckets.size());
175
176 for (auto & bucket : buckets)
177 {
178 bucket.centroids.reserve(bucket.count);
179 bucket.boundary_elements.reserve(bucket.count);
180 _bboxes_by_subdomain.emplace(bucket.subdomain_id, bucket.bbox);
181 }
182
183 for (const auto & elem : boundary_mesh->active_element_ptr_range())
184 {
185 const auto sid = elem->subdomain_id();
186 auto & bucket = buckets[bucket_index_by_subdomain.at(sid)];
187
188 bucket.centroids.emplace_back(elem->vertex_average());
189
190 std::unique_ptr<SurfaceElement> bnd_elem;
191 if (elem->type() == EDGE2)
192 bnd_elem = std::make_unique<SurfaceEdge2>(elem);
193 else if (elem->type() == TRI3)
194 bnd_elem = std::make_unique<SurfaceTri3>(elem);
195 else
196 mooseError("Unsupported element type in SubdomainGrainIDGenerator");
197
198 bucket.boundary_elements.emplace_back(std::move(bnd_elem));
199 }
200
201 for (auto & bucket : buckets)
202 {
203 _centroids_by_subdomain.emplace(bucket.subdomain_id, std::move(bucket.centroids));
204 _boundary_elements_by_subdomain.emplace(bucket.subdomain_id,
205 std::move(bucket.boundary_elements));
206 }
207}
208
209void
211{
214 _checker_entries.clear();
216
217 const SurfaceGeometry::RayDirectionOptions ray_options{
218 _ray_direction.norm_sq() == 0 ? SurfaceGeometry::RayDirectionMode::AUTO_PCA
219 : SurfaceGeometry::RayDirectionMode::USER_SPECIFIED,
221
222 for (const auto & [subdomain_id, elements] : _boundary_elements_by_subdomain)
223 {
224 auto checker = std::make_unique<AdaptiveRayContainmentCheck>(
225 elements, _centroids_by_subdomain.at(subdomain_id), ray_options, _eps, _leaf_max_size);
226
227 auto * checker_ptr = checker.get();
228 _subdomain_id_checkers[subdomain_id] = std::move(checker);
229 _checker_entries.push_back({subdomain_id, _bboxes_by_subdomain.at(subdomain_id), checker_ptr});
230 }
231}
232
233bool
235 const BoundingBox & rhs,
236 const Real tolerance)
237{
238 const Point lhs_min = lhs.min();
239 const Point lhs_max = lhs.max();
240 const Point rhs_min = rhs.min();
241 const Point rhs_max = rhs.max();
242
243 for (const auto d : make_range(LIBMESH_DIM))
244 if (lhs_max(d) + tolerance < rhs_min(d) || rhs_max(d) + tolerance < lhs_min(d))
245 return false;
246
247 return true;
248}
unsigned int count
registerMooseObject("ShiftedBoundaryMethodApp", SubdomainGrainIDGenerator)
void ErrorVector unsigned int
void addRequiredParam(const std::string &name, const std::string &doc_string)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
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 mooseError(Args &&... args) const
const SBMUtils::InterceptedSubdomainPolicy _intercepted_subdomain_policy
Policy for assigning partial elements to a dedicated intercepted subdomain.
const Order _qrule_order
Quadrature order used to estimate the retained-domain fraction.
const Real _lambda
Threshold applied to the fraction outside the retained domain for a partial element.
Common parameters and members for shifted boundary subdomain mesh generators that classify volume ele...
std::unique_ptr< libMesh::MeshBase > & _input
The input mesh to modify.
static InputParameters validParams()
Assigns grain subdomain IDs to a volume mesh based on watertight closed boundaries in an external bou...
Point _ray_direction
Ray shooting direction in Point format.
std::unique_ptr< libMesh::MeshBase > _boundary_owned
The boundary mesh to use for identifying grain IDs.
std::unique_ptr< libMesh::MeshBase > & _boundary_mesh
Reference to the external boundary mesh defining grain boundaries.
void buildInOutTesters()
Build the IN-OUT testers for each subdomain.
SubdomainGrainIDGenerator(const InputParameters &parameters)
std::unique_ptr< libMesh::MeshBase > generate() override
void buildSubdomainGroupedData()
build the data structure grouping by different subdomains
std::unordered_map< SubdomainID, BoundingBox > _bboxes_by_subdomain
The bounding boxes of the boundary elements grouped by subdomains.
std::unordered_map< SubdomainID, std::unique_ptr< AdaptiveRayContainmentCheck > > _subdomain_id_checkers
The IN-OUT testers for each subdomain.
static InputParameters validParams()
static bool boundingBoxesIntersect(const BoundingBox &lhs, const BoundingBox &rhs, const Real tolerance)
Check whether two bounding boxes overlap within a small tolerance.
std::unordered_map< SubdomainID, std::vector< std::unique_ptr< SurfaceElement > > > _boundary_elements_by_subdomain
The boundary elements grouped by subdomains.
unsigned int _leaf_max_size
Maximum number of boundary-element centroids in a leaf node of the k-d tree.
std::unordered_map< SubdomainID, std::vector< Point > > _centroids_by_subdomain
The centroids of the boundary elements grouped by subdomains.
std::vector< SubdomainCheckerEntry > _checker_entries
Cache contiguous checker metadata for faster per-element scans.
Real _eps
eps for intersection or on surface checking
void subdomain_ids(std::set< subdomain_id_type > &ids, const bool global=true) const
std::string & subdomain_name(subdomain_id_type id)
subdomain_id_type n_subdomains() const
MeshBase & mesh
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
static constexpr Real TOLERANCE