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 
74  // Sideset restriction param group
75  params.addParamNamesToGroup(
76  "included_boundaries excluded_boundaries included_subdomains included_neighbors "
77  "include_only_external_sides normal normal_tol",
78  "Sideset restrictions");
79 
80  return params;
81 }
82 
84  : MeshGenerator(parameters),
85  _input(getMesh("input")),
86  _boundary_names(std::vector<BoundaryName>()),
87  _fixed_normal(getParam<bool>("fixed_normal")),
88  _replace(getParam<bool>("replace")),
89  _check_included_boundaries(isParamValid("included_boundaries")),
90  _check_excluded_boundaries(isParamValid("excluded_boundaries")),
91  _check_subdomains(isParamValid("included_subdomains")),
92  _check_neighbor_subdomains(isParamValid("included_neighbors")),
93  _included_boundary_ids(std::vector<boundary_id_type>()),
94  _excluded_boundary_ids(std::vector<boundary_id_type>()),
95  _included_subdomain_ids(std::vector<subdomain_id_type>()),
96  _included_neighbor_subdomain_ids(std::vector<subdomain_id_type>()),
97  _include_only_external_sides(getParam<bool>("include_only_external_sides")),
98  _using_normal(isParamSetByUser("normal")),
99  _normal(_using_normal ? Point(getParam<Point>("normal") / getParam<Point>("normal").norm())
100  : getParam<Point>("normal")),
101  _normal_tol(getParam<Real>("normal_tol"))
102 {
103  if (isParamValid("new_boundary"))
104  _boundary_names = getParam<std::vector<BoundaryName>>("new_boundary");
105 }
106 
108 
109 void
111 {
112  mooseAssert(_fe_face == nullptr, "FE Face has already been initialized");
113 
114  // To know the dimension of the mesh
115  if (!mesh.is_prepared())
116  mesh.prepare_for_use();
117  const auto dim = mesh.mesh_dimension();
118 
119  // Setup the FE Object so we can calculate normals
120  libMesh::FEType fe_type(Utility::string_to_enum<Order>("CONSTANT"),
121  Utility::string_to_enum<libMesh::FEFamily>("MONOMIAL"));
122  _fe_face = libMesh::FEBase::build(dim, fe_type);
123  _qface = std::make_unique<libMesh::QGauss>(dim - 1, FIRST);
124  _fe_face->attach_quadrature_rule(_qface.get());
125  // Must always pre-request quantities you want to compute
126  _fe_face->get_normals();
127 
128  // Handle incompatible parameters
130  paramError("include_only_external_sides", "External sides dont have neighbors");
131 
133  {
134  const auto & included_boundaries = getParam<std::vector<BoundaryName>>("included_boundaries");
135  for (const auto & boundary_name : _boundary_names)
136  if (std::find(included_boundaries.begin(), included_boundaries.end(), boundary_name) !=
137  included_boundaries.end())
138  paramError(
139  "new_boundary",
140  "A boundary cannot be both the new boundary and be included in the list of included "
141  "boundaries. If you are trying to restrict an existing boundary, you must use a "
142  "different name for 'new_boundary', delete the old boundary, and then rename the "
143  "new boundary to the old boundary.");
144 
145  _included_boundary_ids = MooseMeshUtils::getBoundaryIDs(mesh, included_boundaries, false);
146 
147  // Check that the included boundary ids/names exist in the mesh
148  for (const auto i : index_range(_included_boundary_ids))
150  paramError("included_boundaries",
151  "The boundary '",
152  included_boundaries[i],
153  "' was not found within the mesh");
154  }
155 
157  {
158  const auto & excluded_boundaries = getParam<std::vector<BoundaryName>>("excluded_boundaries");
159  for (const auto & boundary_name : _boundary_names)
160  if (std::find(excluded_boundaries.begin(), excluded_boundaries.end(), boundary_name) !=
161  excluded_boundaries.end())
162  paramError(
163  "new_boundary",
164  "A boundary cannot be both the new boundary and be excluded in the list of excluded "
165  "boundaries.");
166  _excluded_boundary_ids = MooseMeshUtils::getBoundaryIDs(mesh, excluded_boundaries, false);
167 
168  // Check that the excluded boundary ids/names exist in the mesh
169  for (const auto i : index_range(_excluded_boundary_ids))
171  paramError("excluded_boundaries",
172  "The boundary '",
173  excluded_boundaries[i],
174  "' was not found within the mesh");
175 
177  {
178  // Check that included and excluded boundary lists do not overlap
179  for (const auto & boundary_id : _included_boundary_ids)
180  if (std::find(_excluded_boundary_ids.begin(), _excluded_boundary_ids.end(), boundary_id) !=
182  paramError("excluded_boundaries",
183  "'included_boundaries' and 'excluded_boundaries' lists should not overlap");
184  }
185  }
186 
187  // Get the boundary ids from the names
188  if (parameters().isParamValid("included_subdomains"))
189  {
190  // check that the subdomains exist in the mesh
191  const auto subdomains = getParam<std::vector<SubdomainName>>("included_subdomains");
192  for (const auto & name : subdomains)
194  paramError("included_subdomains", "The block '", name, "' was not found in the mesh");
195 
197  }
198 
199  if (parameters().isParamValid("included_neighbors"))
200  {
201  // check that the subdomains exist in the mesh
202  const auto subdomains = getParam<std::vector<SubdomainName>>("included_neighbors");
203  for (const auto & name : subdomains)
205  paramError("included_neighbors", "The block '", name, "' was not found in the mesh");
206 
208  }
209 
210  // We will want to Change the below code when we have more fine-grained control over advertising
211  // what we need and how we satisfy those needs. For now we know we need to have neighbors per
212  // #15823...and we do have an explicit `find_neighbors` call...but we don't have a
213  // `neighbors_found` API and it seems off to do:
214  //
215  // if (!mesh.is_prepared())
216  // mesh.find_neighbors()
217 }
218 
219 void
221 {
222  _qface.reset();
223  _fe_face.reset();
224 }
225 
226 void
228  const Point & normal,
229  const boundary_id_type & side_id,
230  MeshBase & mesh)
231 {
232  if (elem == nullptr || elem == remote_elem ||
233  (_visited[side_id].find(elem) != _visited[side_id].end()))
234  return;
235 
236  // Skip if element is not in specified subdomains
238  return;
239 
240  _visited[side_id].insert(elem);
241 
242  // Request to compute normal vectors
243  const std::vector<Point> & face_normals = _fe_face->get_normals();
244 
245  for (const auto side : make_range(elem->n_sides()))
246  {
247 
248  _fe_face->reinit(elem, side);
249  // We'll just use the normal of the first qp
250  const Point face_normal = face_normals[0];
251 
252  if (!elemSideSatisfiesRequirements(elem, side, mesh, normal, face_normal))
253  continue;
254 
255  if (_replace)
256  mesh.get_boundary_info().remove_side(elem, side);
257 
258  mesh.get_boundary_info().add_side(elem, side, side_id);
259  for (const auto neighbor : make_range(elem->n_sides()))
260  {
261  // Flood to the neighboring elements using the current matching side normal from this
262  // element.
263  // This will allow us to tolerate small changes in the normals so we can "paint" around a
264  // curve.
265  flood(elem->neighbor_ptr(neighbor), _fixed_normal ? normal : face_normal, side_id, mesh);
266  }
267  }
268 }
269 
270 bool
272  const Point & normal_2,
273  const Real & tol) const
274 {
275  return (1.0 - normal_1 * normal_2) <= tol;
276 }
277 
278 bool
280  const Elem * const elem, const std::vector<subdomain_id_type> & subdomain_id_list) const
281 {
282  subdomain_id_type curr_subdomain = elem->subdomain_id();
283  return std::find(subdomain_id_list.begin(), subdomain_id_list.end(), curr_subdomain) !=
284  subdomain_id_list.end();
285 }
286 
287 bool
289  const unsigned int side,
290  const MeshBase & mesh) const
291 {
292  for (const auto & bid : _included_boundary_ids)
293  if (mesh.get_boundary_info().has_boundary_id(elem, side, bid))
294  return true;
295  return false;
296 }
297 
298 bool
300  const unsigned int side,
301  const MeshBase & mesh) const
302 {
303  for (const auto bid : _excluded_boundary_ids)
304  if (mesh.get_boundary_info().has_boundary_id(elem, side, bid))
305  return true;
306  return false;
307 }
308 
309 bool
311  const unsigned int side,
312  const MeshBase & mesh,
313  const Point & desired_normal,
314  const Point & face_normal)
315 {
316  // Skip if side has neighbor and we only want external sides
317  if ((elem->neighbor_ptr(side) && _include_only_external_sides))
318  return false;
319 
320  // Skip if side is not part of included boundaries
322  return false;
323  // Skip if side is part of excluded boundaries
325  return false;
326 
327  // Skip if element does not have neighbor in specified subdomains
329  {
330  const Elem * const neighbor = elem->neighbor_ptr(side);
331  // if the neighbor does not exist, then skip this face; we only add sidesets
332  // between existing elems if _check_neighbor_subdomains is true
333  if (!(neighbor && elementSubdomainIdInList(neighbor, _included_neighbor_subdomain_ids)))
334  return false;
335  }
336 
337  if (_using_normal && !normalsWithinTol(desired_normal, face_normal, _normal_tol))
338  return false;
339 
340  return true;
341 }
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.
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
Find a value in an array.
Definition: KokkosUtils.h:30
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...
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:439
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 InputParameters & parameters() const
Get the parameters of the object.
Definition: MooseBase.h:131
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:162
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.
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...
const std::string & name() const
Get the name of the class.
Definition: MooseBase.h:103
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
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.
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)
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 ...
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
Definition: MooseBase.h:199
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:33
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