https://mooseframework.inl.gov
SideSetsGeneratorBase.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 "SideSetsGeneratorBase.h"
11 #include "Parser.h"
12 #include "InputParameters.h"
13 #include "MooseMesh.h"
14 #include "MooseMeshUtils.h"
15 
16 #include "libmesh/mesh_generation.h"
17 #include "libmesh/mesh.h"
18 #include "libmesh/string_to_enum.h"
19 #include "libmesh/quadrature_gauss.h"
20 #include "libmesh/point_locator_base.h"
21 #include "libmesh/elem.h"
22 #include "libmesh/remote_elem.h"
23 
26 {
28  params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify");
29  params.addRequiredParam<std::vector<BoundaryName>>(
30  "new_boundary", "The list of boundary names to create on the supplied subdomain");
31  params.addParam<bool>("fixed_normal",
32  false,
33  "This Boolean determines whether we fix our normal "
34  "or allow it to vary to \"paint\" around curves");
35 
36  params.addParam<bool>("replace",
37  false,
38  "If true, replace the old sidesets. If false, the current sidesets (if "
39  "any) will be preserved.");
40 
41  params.addParam<std::vector<BoundaryName>>(
42  "included_boundaries",
43  "A set of boundary names or ids whose sides will be included in the new sidesets. A side "
44  "is only added if it also belongs to one of these boundaries.");
45  params.addParam<std::vector<BoundaryName>>(
46  "excluded_boundaries",
47  "A set of boundary names or ids whose sides will be excluded from the new sidesets. A side "
48  "is only added if does not belong to any of these boundaries.");
49  params.addParam<std::vector<SubdomainName>>(
50  "included_subdomains",
51  "A set of subdomain names or ids whose sides will be included in the new sidesets. A side "
52  "is only added if the subdomain id of the corresponding element is in this set.");
53  params.addParam<std::vector<SubdomainName>>("included_neighbors",
54  "A set of neighboring subdomain names or ids. A face "
55  "is only added if the subdomain id of the "
56  "neighbor is in this set");
57  params.addParam<bool>(
58  "include_only_external_sides",
59  false,
60  "Whether to only include external sides when considering sides to add to the sideset");
61 
62  params.addParam<Point>("normal",
63  Point(),
64  "If supplied, only faces with normal equal to this, up to "
65  "normal_tol, will be added to the sidesets specified");
66  params.addRangeCheckedParam<Real>("normal_tol",
67  0.1,
68  "normal_tol>=0 & normal_tol<=2",
69  "If normal is supplied then faces are "
70  "only added if face_normal.normal_hat >= "
71  "1 - normal_tol, where normal_hat = "
72  "normal/|normal|");
73  params.addParam<Real>("variance", "The variance allowed when comparing normals");
74  params.deprecateParam("variance", "normal_tol", "4/01/2025");
75 
76  // Sideset restriction param group
77  params.addParamNamesToGroup(
78  "included_boundaries excluded_boundaries included_subdomains included_neighbors "
79  "include_only_external_sides normal normal_tol",
80  "Sideset restrictions");
81 
82  return params;
83 }
84 
86  : MeshGenerator(parameters),
87  _input(getMesh("input")),
88  _boundary_names(std::vector<BoundaryName>()),
89  _fixed_normal(getParam<bool>("fixed_normal")),
90  _replace(getParam<bool>("replace")),
91  _check_included_boundaries(isParamValid("included_boundaries")),
92  _check_excluded_boundaries(isParamValid("excluded_boundaries")),
93  _check_subdomains(isParamValid("included_subdomains")),
94  _check_neighbor_subdomains(isParamValid("included_neighbors")),
95  _included_boundary_ids(std::vector<boundary_id_type>()),
96  _excluded_boundary_ids(std::vector<boundary_id_type>()),
97  _included_subdomain_ids(std::vector<subdomain_id_type>()),
98  _included_neighbor_subdomain_ids(std::vector<subdomain_id_type>()),
99  _include_only_external_sides(getParam<bool>("include_only_external_sides")),
100  _using_normal(isParamSetByUser("normal")),
101  _normal(_using_normal ? Point(getParam<Point>("normal") / getParam<Point>("normal").norm())
102  : getParam<Point>("normal")),
103  _normal_tol(getParam<Real>("normal_tol"))
104 {
105  if (isParamValid("new_boundary"))
106  _boundary_names = getParam<std::vector<BoundaryName>>("new_boundary");
107 }
108 
110 
111 void
113 {
114  mooseAssert(_fe_face == nullptr, "FE Face has already been initialized");
115 
116  // To know the dimension of the mesh
117  if (!mesh.is_prepared())
118  mesh.prepare_for_use();
119  const auto dim = mesh.mesh_dimension();
120 
121  // Setup the FE Object so we can calculate normals
122  libMesh::FEType fe_type(Utility::string_to_enum<Order>("CONSTANT"),
123  Utility::string_to_enum<libMesh::FEFamily>("MONOMIAL"));
124  _fe_face = libMesh::FEBase::build(dim, fe_type);
125  _qface = std::make_unique<libMesh::QGauss>(dim - 1, FIRST);
126  _fe_face->attach_quadrature_rule(_qface.get());
127  // Must always pre-request quantities you want to compute
128  _fe_face->get_normals();
129 
130  // Handle incompatible parameters
132  paramError("include_only_external_sides", "External sides dont have neighbors");
133 
135  {
136  const auto & included_boundaries = getParam<std::vector<BoundaryName>>("included_boundaries");
137  for (const auto & boundary_name : _boundary_names)
138  if (std::find(included_boundaries.begin(), included_boundaries.end(), boundary_name) !=
139  included_boundaries.end())
140  paramError(
141  "new_boundary",
142  "A boundary cannot be both the new boundary and be included in the list of included "
143  "boundaries. If you are trying to restrict an existing boundary, you must use a "
144  "different name for 'new_boundary', delete the old boundary, and then rename the "
145  "new boundary to the old boundary.");
146 
147  _included_boundary_ids = MooseMeshUtils::getBoundaryIDs(mesh, included_boundaries, false);
148 
149  // Check that the included boundary ids/names exist in the mesh
150  for (const auto i : index_range(_included_boundary_ids))
152  paramError("included_boundaries",
153  "The boundary '",
154  included_boundaries[i],
155  "' was not found within the mesh");
156  }
157 
159  {
160  const auto & excluded_boundaries = getParam<std::vector<BoundaryName>>("excluded_boundaries");
161  for (const auto & boundary_name : _boundary_names)
162  if (std::find(excluded_boundaries.begin(), excluded_boundaries.end(), boundary_name) !=
163  excluded_boundaries.end())
164  paramError(
165  "new_boundary",
166  "A boundary cannot be both the new boundary and be excluded in the list of excluded "
167  "boundaries.");
168  _excluded_boundary_ids = MooseMeshUtils::getBoundaryIDs(mesh, excluded_boundaries, false);
169 
170  // Check that the excluded boundary ids/names exist in the mesh
171  for (const auto i : index_range(_excluded_boundary_ids))
173  paramError("excluded_boundaries",
174  "The boundary '",
175  excluded_boundaries[i],
176  "' was not found within the mesh");
177 
179  {
180  // Check that included and excluded boundary lists do not overlap
181  for (const auto & boundary_id : _included_boundary_ids)
182  if (std::find(_excluded_boundary_ids.begin(), _excluded_boundary_ids.end(), boundary_id) !=
184  paramError("excluded_boundaries",
185  "'included_boundaries' and 'excluded_boundaries' lists should not overlap");
186  }
187  }
188 
189  // Get the boundary ids from the names
190  if (parameters().isParamValid("included_subdomains"))
191  {
192  // check that the subdomains exist in the mesh
193  const auto subdomains = getParam<std::vector<SubdomainName>>("included_subdomains");
194  for (const auto & name : subdomains)
196  paramError("included_subdomains", "The block '", name, "' was not found in the mesh");
197 
199  }
200 
201  if (parameters().isParamValid("included_neighbors"))
202  {
203  // check that the subdomains exist in the mesh
204  const auto subdomains = getParam<std::vector<SubdomainName>>("included_neighbors");
205  for (const auto & name : subdomains)
207  paramError("included_neighbors", "The block '", name, "' was not found in the mesh");
208 
210  }
211 
212  // We will want to Change the below code when we have more fine-grained control over advertising
213  // what we need and how we satisfy those needs. For now we know we need to have neighbors per
214  // #15823...and we do have an explicit `find_neighbors` call...but we don't have a
215  // `neighbors_found` API and it seems off to do:
216  //
217  // if (!mesh.is_prepared())
218  // mesh.find_neighbors()
219 }
220 
221 void
223 {
224  _qface.reset();
225  _fe_face.reset();
226 }
227 
228 void
230  const Point & normal,
231  const boundary_id_type & side_id,
232  MeshBase & mesh)
233 {
234  if (elem == nullptr || elem == remote_elem ||
235  (_visited[side_id].find(elem) != _visited[side_id].end()))
236  return;
237 
238  // Skip if element is not in specified subdomains
240  return;
241 
242  _visited[side_id].insert(elem);
243 
244  // Request to compute normal vectors
245  const std::vector<Point> & face_normals = _fe_face->get_normals();
246 
247  for (const auto side : make_range(elem->n_sides()))
248  {
249 
250  _fe_face->reinit(elem, side);
251  // We'll just use the normal of the first qp
252  const Point face_normal = face_normals[0];
253 
254  if (!elemSideSatisfiesRequirements(elem, side, mesh, normal, face_normal))
255  continue;
256 
257  if (_replace)
258  mesh.get_boundary_info().remove_side(elem, side);
259 
260  mesh.get_boundary_info().add_side(elem, side, side_id);
261  for (const auto neighbor : make_range(elem->n_sides()))
262  {
263  // Flood to the neighboring elements using the current matching side normal from this
264  // element.
265  // This will allow us to tolerate small changes in the normals so we can "paint" around a
266  // curve.
267  flood(elem->neighbor_ptr(neighbor), _fixed_normal ? normal : face_normal, side_id, mesh);
268  }
269  }
270 }
271 
272 bool
274  const Point & normal_2,
275  const Real & tol) const
276 {
277  return (1.0 - normal_1 * normal_2) <= tol;
278 }
279 
280 bool
282  const Elem * const elem, const std::vector<subdomain_id_type> & subdomain_id_list) const
283 {
284  subdomain_id_type curr_subdomain = elem->subdomain_id();
285  return std::find(subdomain_id_list.begin(), subdomain_id_list.end(), curr_subdomain) !=
286  subdomain_id_list.end();
287 }
288 
289 bool
291  const unsigned int side,
292  const MeshBase & mesh) const
293 {
294  for (const auto & bid : _included_boundary_ids)
295  if (mesh.get_boundary_info().has_boundary_id(elem, side, bid))
296  return true;
297  return false;
298 }
299 
300 bool
302  const unsigned int side,
303  const MeshBase & mesh) const
304 {
305  for (const auto bid : _excluded_boundary_ids)
306  if (mesh.get_boundary_info().has_boundary_id(elem, side, bid))
307  return true;
308  return false;
309 }
310 
311 bool
313  const unsigned int side,
314  const MeshBase & mesh,
315  const Point & desired_normal,
316  const Point & face_normal)
317 {
318  // Skip if side has neighbor and we only want external sides
319  if ((elem->neighbor_ptr(side) && _include_only_external_sides))
320  return false;
321 
322  // Skip if side is not part of included boundaries
324  return false;
325  // Skip if side is part of excluded boundaries
327  return false;
328 
329  // Skip if element does not have neighbor in specified subdomains
331  {
332  const Elem * const neighbor = elem->neighbor_ptr(side);
333  // if the neighbor does not exist, then skip this face; we only add sidesets
334  // between existing elems if _check_neighbor_subdomains is true
335  if (!(neighbor && elementSubdomainIdInList(neighbor, _included_neighbor_subdomain_ids)))
336  return false;
337  }
338 
339  if (_using_normal && !normalsWithinTol(desired_normal, face_normal, _normal_tol))
340  return false;
341 
342  return true;
343 }
bool elementSubdomainIdInList(const Elem *const elem, const std::vector< subdomain_id_type > &subdomain_id_list) const
Determines whether the given element&#39;s subdomain id is in the given subdomain_id_list.
std::unique_ptr< FEGenericBase< Real > > build(const unsigned int dim, const FEType &fet)
const bool _fixed_normal
Whether to fix the normal or allow it to vary to "paint" around curves.
bool elemSideSatisfiesRequirements(const Elem *const elem, const unsigned int side, const MeshBase &mesh, const Point &normal, const Point &face_normal)
Determines whether the given element&#39;s side satisfies the following parameters: include_only_external...
const Real _normal_tol
if normal is specified, then faces are only added if face_normal.normal_hat <= 1 - normal_tol where n...
const BoundaryID INVALID_BOUNDARY_ID
Definition: MooseTypes.C:22
std::vector< subdomain_id_type > _included_neighbor_subdomain_ids
A list of included neighbor subdomain ids that the sides&#39; neighbor element must be a part of...
FIRST
const boundary_id_type side_id
SideSetsGeneratorBase(const InputParameters &parameters)
MeshBase & mesh
const bool _check_included_boundaries
whether to check boundary ids against the included boundary list when adding sides or not ...
const bool _replace
Whether or not to remove the old sidesets (all of them, if any) when adding sidesets.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
static constexpr std::size_t dim
This is the dimension of all vector and tensor datastructures used in MOOSE.
Definition: Moose.h:153
bool normalsWithinTol(const Point &normal_1, const Point &normal_2, const Real &tol) const
Determines whether two normal vectors are within normal_tol of each other.
std::vector< BoundaryName > _boundary_names
The list of new boundary names.
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.
virtual const std::string & name() const
Get the name of the class.
Definition: MooseBase.h:57
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...
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
std::vector< subdomain_id_type > _included_subdomain_ids
A list of included subdomain ids that the side has to be part of, extracted from the included_subdoma...
const bool _include_only_external_sides
Whether to only include external side when considering sides to add to the sideset.
int8_t boundary_id_type
void deprecateParam(const std::string &old_name, const std::string &new_name, const std::string &removal_date)
const bool _check_excluded_boundaries
whether to check boundary ids against the excluded boundary list when adding sides or not ...
void flood(const Elem *elem, const Point &normal, const boundary_id_type &side_id, MeshBase &mesh)
This method implements a recursive flood routine to paint a sideset of mesh to neighboring faces give...
bool elementSideInIncludedBoundaries(const Elem *const elem, const unsigned int side, const MeshBase &mesh) const
Determines whether the given side of an element belongs to any boundaries in the included_boundaries ...
std::vector< BoundaryID > getBoundaryIDs(const libMesh::MeshBase &mesh, const std::vector< BoundaryName > &boundary_name, bool generate_unknown, const std::set< BoundaryID > &mesh_boundary_ids)
Gets the boundary IDs with their names.
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 ...
auto norm(const T &a) -> decltype(std::abs(a))
static InputParameters validParams()
Definition: MeshGenerator.C:23
void finalize()
This method finalizes the object, setting names back in the boundary_info object and releasing memory...
static InputParameters validParams()
bool _using_normal
true if only faces close to "normal" will be added
std::vector< boundary_id_type > _included_boundary_ids
A list of boundary ids that the side has to be part of, extracted from the included_boundaries parame...
bool hasSubdomainName(const MeshBase &input_mesh, const SubdomainName &name)
Whether a particular subdomain name exists in the mesh.
const bool _check_subdomains
whether to check subdomain ids of the element in the (element, side, boundary id) tuple when adding s...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)
const InputParameters & parameters() const
Get the parameters of the object.
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...
std::vector< boundary_id_type > _excluded_boundary_ids
A list of boundary ids that the side must not be a part of, extracted from the excluded_boundaries pa...
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
std::unique_ptr< libMesh::QGauss > _qface
bool elementSideInExcludedBoundaries(const Elem *const elem, const unsigned int side, const MeshBase &mesh) const
Determines whether the given side of an element belongs to any boundaries in the excluded_boundaries ...
const bool _check_neighbor_subdomains
whether to check the subdomain ids of the neighbor element (on the other &#39;side&#39; of the side) when add...
std::map< boundary_id_type, std::set< const Elem * > > _visited
std::unique_ptr< libMesh::FEBase > _fe_face
MeshGenerators are objects that can modify or add to an existing mesh.
Definition: MeshGenerator.h:32
void setup(MeshBase &mesh)
This method is used to construct the FE object so we can compute normals of faces.
auto index_range(const T &sizable)
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...
bool isParamValid(const std::string &name) const
This method returns parameters that have been initialized in one fashion or another, i.e.
const RemoteElem * remote_elem