https://mooseframework.inl.gov
Loading...
Searching...
No Matches
SurfaceMeshGeneratorBase.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
11#include "InputParameters.h"
12#include "MooseMeshUtils.h"
13#include "MeshTraversingUtils.h"
14
15#include "libmesh/mesh_generation.h"
16#include "libmesh/mesh.h"
17#include "libmesh/elem.h"
18#include "libmesh/remote_elem.h"
19#include "libmesh/string_to_enum.h"
20
23{
25 params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify");
26 // NOTE: don't forget to suppress this if not using this base class to create subdomains
27 params.addRequiredParam<std::vector<SubdomainName>>(
28 "new_subdomain", "The list of subdomain names to create on the supplied subdomain");
29 params.addParam<std::vector<SubdomainName>>(
30 "included_subdomains",
31 "A set of subdomain names or ids an element has to be previously a part of to be considered "
32 "for modification.");
33
34 // Painting/flooding parameters
35 // Using normals of the surface element
36 params.addParam<Point>("normal",
37 Point(),
38 "If supplied, only surface (2D) elements with normal equal to this, up to "
39 "normal_tol, will be considered for modification");
40 params.addRangeCheckedParam<Real>("normal_tol",
41 0.1,
42 "normal_tol>=0 & normal_tol<=2",
43 "Surface elements are "
44 "only added if face_normal.normal_hat >= "
45 "1 - normal_tol, where normal_hat = "
46 "normal/|normal|. The normal can the normal specified by the "
47 "parameter or by a specific mesh generator.");
48 params.addParam<bool>(
49 "fixed_normal",
50 false,
51 "Whether to move the normal vector as we paint/flood the geometry, or keep it "
52 "fixed from the first element we started painting with");
53 params.addParam<bool>("check_painted_neighbor_normals",
54 false,
55 "When examining the normal of a 2D element and comparing to the 'painting' "
56 "normal, also check if the element neighbors in the 'painted' subdomain "
57 "have a closer normal and accept the element into the new subdomain if so");
58
59 // Parameters for handling surface elemets with flipped normals. These are unfortunately quite
60 // common in STL files, and if we do not account for them the flooding algorithm often floods
61 // elements all around the flipped normal element, creating a single-element patch 'hole' inside
62 // the larger patch!
63 params.addRangeCheckedParam<Real>("flipped_normal_tol",
64 0.1,
65 "flipped_normal_tol>=0 & flipped_normal_tol<=2",
66 "If 'consider_flipped_normals' is true, surface elements are "
67 "also added if -1 * face_normal.normal_hat >= "
68 "1 - normal_tol, where normal_hat = "
69 "normal/|normal|");
70 params.addParam<bool>(
71 "consider_flipped_normals",
72 false,
73 "Whether to allow for surface elements to be considered "
74 "when their normal is flipped with regard to the neighbor element we are painting from. A "
75 "specific tolerance may be specified for these flipped normals using the "
76 "'flipped_normal_tol'.");
77 params.addParam<bool>(
78 "flip_inverted_normals", false, "Whether to flip the elements with inverted normals");
79
80 // Other painting / flooding parameters
81 params.addParam<std::vector<Real>>(
82 "max_paint_size_centroids",
83 "Maximum distance between element centroids (using a vertex average approximation) "
84 "when painting/flooding the geometry. 0 means not applying a distance constraint, a single "
85 "value in the vector is applied to all elements, multiple values can be specified for each "
86 "'included_subdomains'");
87 params.addParam<bool>(
88 "flood_elements_once",
89 false,
90 "Whether to consider elements only once when painting/flooding the geometry");
91 params.addParam<unsigned int>(
92 "flood_max_recursion",
93 1e5,
94 "Max number of recursions when flooding. Once this number is reached, the propagation is "
95 "stopped until enough calls have returned");
96
97 params.addParamNamesToGroup("normal normal_tol fixed_normal check_painted_neighbor_normals",
98 "Flooding using surface element normals");
99 params.addParamNamesToGroup("consider_flipped_normals flipped_normal_tol",
100 "Flooding using surface element inverted normals");
101 params.addParamNamesToGroup("max_paint_size_centroids flood_elements_once", "Other flooding");
102 return params;
103}
104
106 : MeshGenerator(parameters),
107 _input(getMesh("input")),
108 _subdomain_names(isParamValid("new_subdomain")
109 ? getParam<std::vector<SubdomainName>>("new_subdomain")
110 : std::vector<SubdomainName>()),
111 _check_subdomains(isParamValid("included_subdomains")),
112 _included_subdomain_ids(std::vector<subdomain_id_type>()),
113 _using_normal(isParamSetByUser("normal") || isParamValid("_using_normal")),
114 _normal(isParamSetByUser("normal")
115 ? Point(getParam<Point>("normal") / getParam<Point>("normal").norm())
116 : getParam<Point>("normal")),
117 _normal_tol(getParam<Real>("normal_tol")),
118 _flipped_normal_tol(getParam<Real>("flipped_normal_tol")),
119 _fixed_flooding_normal(getParam<bool>("fixed_normal")),
120 _consider_flipped_normals(getParam<bool>("consider_flipped_normals")),
121 _flip_inverted_normals(getParam<bool>("flip_inverted_normals")),
122 _has_max_distance_criterion(isParamSetByUser("max_paint_size_centroids")),
123 _flood_only_once(getParam<bool>("flood_elements_once")),
124 _flood_max_recursion(getParam<unsigned int>("flood_max_recursion")),
125 _check_painted_neighor_normals(getParam<bool>("check_painted_neighbor_normals"))
126{
127}
128
129void
131{
132 // To know the dimension of the mesh
133 if (!mesh.preparation().has_cached_elem_data)
134 mesh.cache_elem_data();
135
136 // Get the subdomain ids from the names
137 if (isParamValid("included_subdomains"))
138 {
139 // check that the subdomains exist in the mesh
140 const auto & subdomains = getParam<std::vector<SubdomainName>>("included_subdomains");
141 for (const auto & name : subdomains)
143 paramError("included_subdomains", "The block '", name, "' was not found in the mesh");
144
146 }
147
148 // Set up the max elem-to-elem distance map
150 {
151 const auto & max_dists = getParam<std::vector<Real>>("max_paint_size_centroids");
152 if (max_dists.size() != _included_subdomain_ids.size() && max_dists.size() != 1)
153 paramError("max_paint_size_centroids",
154 "Maximum distance should be specified uniformly for all subdomains (1 value) or a "
155 "value for each 'included_subdomains'");
156
157 if (_included_subdomain_ids.size())
158 for (const auto i : index_range(_included_subdomain_ids))
159 // Single value is applied for all subdomains
160 // 0 is translated to a very big number which therefore won't impose the criterion
162 (max_dists.size() == 1)
163 ? max_dists[0]
164 : (max_dists[i] > 0 ? max_dists[i]
165 : std::pow(std::numeric_limits<Real>::max(), 0.3));
166 else
167 _max_elem_distance[static_cast<subdomain_id_type>(-1)] = max_dists[0];
168 }
169
170 // Clear the maps used to count visits
171 _visited.clear();
172 _acted_upon_once.clear();
173}
174
175void
177 const Point & base_normal,
178 const Elem & starting_elem,
179 const subdomain_id_type & sub_id,
180 MeshBase & mesh)
181{
182 // Avoid re-considering the same elements
183 if (elem == nullptr || elem == remote_elem ||
184 (_visited[sub_id].find(elem) != _visited[sub_id].end()))
185 return;
186 if (_flood_only_once && _acted_upon_once.count(elem))
187 return;
188
189 // Skip if element is not in specified subdomains
190 if (_check_subdomains &&
192 return;
193
194 _visited[sub_id].insert(elem);
195 auto elem_normal = get2DElemNormal(elem);
196
197 bool criterion_met = false;
198 if (elementSatisfiesRequirements(elem, base_normal, starting_elem, elem_normal))
199 criterion_met = true;
200 // Additional heuristic based on neighbor normal vs element normal
202 {
203 // Try to flood from each side with the same subdomain
204 for (const auto neighbor : make_range(elem->n_sides()))
205 if (elem->neighbor_ptr(neighbor) &&
206 (elem->neighbor_ptr(neighbor)->subdomain_id() == sub_id) &&
208 elem, get2DElemNormal(elem->neighbor_ptr(neighbor)), starting_elem, elem_normal))
209 criterion_met = true;
210 }
211
212 if (!criterion_met)
213 return;
214
215 // Modify the subdomain
216 // TODO: consider working with base class attributes rather than arguments
217 actOnElem(elem, base_normal, sub_id, mesh);
218
219 // We don't want to remove the element from consideration too early
220 _acted_upon_once.insert(elem);
221
222 // Flip the element if needed
223 // NOTE: we have already passed "elementSatisfiesRequirements" here
224 if (_consider_flipped_normals && base_normal * elem_normal < 0)
225 {
226 elem_normal *= -1;
227 BoundaryInfo & boundary_info = mesh.get_boundary_info();
228
230 elem->flip(&boundary_info);
231 }
232
233 for (const auto neighbor : make_range(elem->n_sides()))
234 {
236 // Flood to the neighboring elements
238 flood(elem->neighbor_ptr(neighbor),
239 _fixed_flooding_normal ? base_normal : elem_normal,
240 starting_elem,
241 sub_id,
242 mesh);
244 }
245}
246
247bool
249 const Point & desired_normal,
250 const Elem & base_elem,
251 const Point & face_normal) const
252{
253 // False if element is not in specified subdomains
254 if (_check_subdomains &&
256 return false;
257
258 // False if normal does not meet criteria
259 if (_using_normal &&
260 (!MeshTraversingUtils::normalsWithinTol(desired_normal, face_normal, _normal_tol) &&
262 !MeshTraversingUtils::normalsWithinTol(desired_normal, -face_normal, _flipped_normal_tol))))
263 return false;
264
265 // False if exceeding the patch size
267 {
268 // The subdomain from which the element to paint over comes from is used to find the limitation
269 // on the radius, which will effectively be applied onto the new subdomains (to which base_elem
270 // already belongs)
271 const auto max_dsq =
273 ? MathUtils::pow(libmesh_map_find(_max_elem_distance, elem->subdomain_id()), 2)
275 libmesh_map_find(_max_elem_distance, static_cast<subdomain_id_type>(-1)), 2);
276 if ((elem->vertex_average() - base_elem.vertex_average()).norm_sq() > max_dsq)
277 return false;
278 }
279
280 return true;
281}
282
283Point
284SurfaceMeshGeneratorBase::get2DElemNormal(const Elem * const elem) const
285{
286 mooseAssert(elem->dim() == 2, "Should be a 2D element");
287 mooseAssert(elem->default_order() == FIRST, "Should be a first order element");
288 // TODO: add support for non-planar element
289 // TODO: optimize to avoid the allocation
290 if (elem->n_nodes() > 3)
291 {
292 std::vector<Point> vec_pts(elem->n_nodes());
293 for (const auto & node : elem->node_ref_range())
294 vec_pts.push_back(node);
295 if (!MooseMeshUtils::isCoPlanar(vec_pts))
296 mooseDoOnce(mooseWarning("A non planar element was detected. Normal is approximated with the "
297 "first 3 nodes at time."););
298 }
299
300 const auto & p1 = elem->point(0);
301 const auto & p2 = elem->point(1);
302 const auto & p3 = elem->point(2);
303 auto elem_normal = (p1 - p2).cross(p1 - p3);
304 if (elem_normal.norm_sq() == 0)
305 {
306 mooseWarning("Colinear nodes on element " + std::to_string(elem->id()) + ", using 0 normal");
307 return elem_normal;
308 }
309 return elem_normal.unit();
310}
void ErrorVector unsigned int
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system.
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
This method takes a space delimited list of parameter names and adds them to the specified group name...
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object.
void addRequiredParam(const std::string &name, const std::string &doc_string)
This method adds a parameter and documentation string to the InputParameters object that will be extr...
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
MeshGenerators are objects that can modify or add to an existing mesh.
static InputParameters validParams()
const std::string & name() const
Get the name of the class.
Definition MooseBase.h:103
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
Definition MooseBase.h:457
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
Definition MooseBase.h:199
void mooseWarning(Args &&... args) const
const Real _normal_tol
Tolerance to group elements with normals such that face_normal.normal_hat <= 1 - normal_tol where nor...
Point get2DElemNormal(const Elem *const elem) const
Get the normal of the 2D element.
const bool _fixed_flooding_normal
Whether to paint/flood using a fixed normal or a moving normal.
const bool _has_max_distance_criterion
Whether to painting beyond a certain radius.
void flood(Elem *const elem, const Point &normal, const Elem &starting_elem, const subdomain_id_type &sub_id, MeshBase &mesh)
This method implements a recursive flood routine to paint (applying an operation) to elements on mesh...
std::unordered_set< Elem * > _acted_upon_once
Set used when flooding each element once. If the element pointer is in the set, it has been visited a...
const bool _check_subdomains
whether to check the prior subdomain id of the element when choosing whether to change its subdomain ...
SurfaceMeshGeneratorBase(const InputParameters &parameters)
static InputParameters validParams()
void setup(MeshBase &mesh)
Sets up various data structures.
const Real _flipped_normal_tol
Tolerance but when using the flipped normal.
std::vector< subdomain_id_type > _included_subdomain_ids
A list of included subdomain ids that the element has to be priorly a part of, extracted from the 'in...
virtual void actOnElem(Elem *const elem, const Point &normal, const subdomain_id_type &sub_id, MeshBase &mesh)=0
Action to perform when flooding.
bool _using_normal
true if only elements are only considered when their normal is close to either the "_normal" or a mov...
bool _flood_only_once
Only act on each element once.
const bool _check_painted_neighor_normals
Additional heuristic: check the element neighbors and if they have already been painted with the subd...
const unsigned int _flood_max_recursion
Maximum amount of calls to the flood routine at once.
bool elementSatisfiesRequirements(const Elem *const elem, const Point &desired_normal, const Elem &base_elem, const Point &face_normal) const
Determines whether the given element satisfies a set of criteria that are defined in this base class.
std::unordered_map< subdomain_id_type, std::unordered_set< Elem * > > _visited
Map used for the flooding algorithm to keep track of which elements have been visited for which subdo...
unsigned int _flood_recursion_count
Current tally for the number of flood routine calls active.
std::unordered_map< subdomain_id_type, Real > _max_elem_distance
Distance to use for max painting radius. This distance can be specified per subdomain.
const bool _flip_inverted_normals
Whether to flip the normal of a surface element when they meet the criterion.
const bool _consider_flipped_normals
Whether to also consider surface elements that have a flipped normal.
MeshBase & mesh
T pow(T x, int e)
Definition MathUtils.h:91
bool elementSubdomainIdInList(const Elem *const elem, const std::vector< subdomain_id_type > &subdomain_id_list)
Determines whether the given element's subdomain id is in the given subdomain_id_list.
bool normalsWithinTol(const Point &normal_1, const Point &normal_2, const Real tol)
Determines whether two normal vectors are within normal_tol of each other.
bool hasSubdomainName(const MeshBase &input_mesh, const SubdomainName &name)
Whether a particular subdomain name exists in the mesh.
std::vector< subdomain_id_type > getSubdomainIDs(const libMesh::MeshBase &mesh, const std::vector< SubdomainName > &subdomain_name)
Get the associated subdomainIDs for the subdomain names that are passed in.
bool isCoPlanar(const std::vector< Point > &vec_pts, const Point plane_nvec, const Point fixed_pt)
Decides whether all the Points of a vector of Points are in a plane that is defined by a normal vecto...
MooseUnits pow(const MooseUnits &, int)
Definition Units.C:537