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 #include "MeshTraversingUtils.h"
16 
17 #include "libmesh/mesh_generation.h"
18 #include "libmesh/mesh.h"
19 #include "libmesh/string_to_enum.h"
20 #include "libmesh/quadrature_gauss.h"
21 #include "libmesh/point_locator_base.h"
22 #include "libmesh/elem.h"
23 #include "libmesh/remote_elem.h"
24 
27 {
29  params.addRequiredParam<MeshGeneratorName>("input", "The mesh we want to modify");
30  params.addRequiredParam<std::vector<BoundaryName>>(
31  "new_boundary", "The list of boundary names to create on the supplied subdomain");
32  params.addParam<bool>("fixed_normal",
33  false,
34  "This Boolean determines whether we fix our normal "
35  "or allow it to vary to \"paint\" around curves");
36 
37  params.addParam<bool>("replace",
38  false,
39  "If true, replace the old sidesets. If false, the current sidesets (if "
40  "any) will be preserved.");
41 
42  params.addParam<std::vector<BoundaryName>>(
43  "included_boundaries",
44  "A set of boundary names or ids whose sides will be included in the new sidesets. A side "
45  "is only added if it also belongs to one of these boundaries.");
46  params.addParam<std::vector<BoundaryName>>(
47  "excluded_boundaries",
48  "A set of boundary names or ids whose sides will be excluded from the new sidesets. A side "
49  "is only added if does not belong to any of these boundaries.");
50  params.addParam<std::vector<SubdomainName>>(
51  "included_subdomains",
52  "A set of subdomain names or ids whose sides will be included in the new sidesets. A side "
53  "is only added if the subdomain id of the corresponding element is in this set.");
54  params.addParam<std::vector<SubdomainName>>("included_neighbors",
55  "A set of neighboring subdomain names or ids. A face "
56  "is only added if the subdomain id of the "
57  "neighbor is in this set");
58  params.addParam<bool>(
59  "include_only_external_sides",
60  false,
61  "Whether to only include external sides when considering sides to add to the sideset");
62 
63  params.addParam<Point>("normal",
64  Point(),
65  "If supplied, only faces with normal equal to this, up to "
66  "normal_tol, will be added to the sidesets specified");
67  params.addRangeCheckedParam<Real>("normal_tol",
68  0.1,
69  "normal_tol>=0 & normal_tol<=2",
70  "If normal is supplied then faces are "
71  "only added if face_normal.normal_hat >= "
72  "1 - normal_tol, where normal_hat = "
73  "normal/|normal|");
74 
75  // Sideset restriction param group
76  params.addParamNamesToGroup(
77  "included_boundaries excluded_boundaries included_subdomains included_neighbors "
78  "include_only_external_sides normal normal_tol",
79  "Sideset restrictions");
80 
81  return params;
82 }
83 
85  : MeshGenerator(parameters),
86  _input(getMesh("input")),
87  _boundary_names(std::vector<BoundaryName>()),
88  _fixed_normal(getParam<bool>("fixed_normal")),
89  _replace(getParam<bool>("replace")),
90  _check_included_boundaries(isParamValid("included_boundaries")),
91  _check_excluded_boundaries(isParamValid("excluded_boundaries")),
92  _check_subdomains(isParamValid("included_subdomains")),
93  _check_neighbor_subdomains(isParamValid("included_neighbors")),
94  _included_boundary_ids(std::vector<boundary_id_type>()),
95  _excluded_boundary_ids(std::vector<boundary_id_type>()),
96  _included_subdomain_ids(std::vector<subdomain_id_type>()),
97  _included_neighbor_subdomain_ids(std::vector<subdomain_id_type>()),
98  _include_only_external_sides(getParam<bool>("include_only_external_sides")),
99  _using_normal(isParamSetByUser("normal")),
100  _normal(_using_normal ? Point(getParam<Point>("normal") / getParam<Point>("normal").norm())
101  : getParam<Point>("normal")),
102  _normal_tol(getParam<Real>("normal_tol"))
103 {
104  if (isParamValid("new_boundary"))
105  _boundary_names = getParam<std::vector<BoundaryName>>("new_boundary");
106 }
107 
109 
110 void
112 {
113  mooseAssert(_fe_face == nullptr, "FE Face has already been initialized");
114 
115  // To know the dimension of the mesh
116  if (!mesh.is_prepared())
117  mesh.prepare_for_use();
118  const auto dim = mesh.mesh_dimension();
119 
120  // Setup the FE Object so we can calculate normals
121  libMesh::FEType fe_type(Utility::string_to_enum<Order>("CONSTANT"),
122  Utility::string_to_enum<libMesh::FEFamily>("MONOMIAL"));
123  _fe_face = libMesh::FEBase::build(dim, fe_type);
124  _qface = std::make_unique<libMesh::QGauss>(dim - 1, FIRST);
125  _fe_face->attach_quadrature_rule(_qface.get());
126  // Must always pre-request quantities you want to compute
127  _fe_face->get_normals();
128 
129  // Handle incompatible parameters
131  paramError("include_only_external_sides", "External sides dont have neighbors");
132 
134  {
135  const auto & included_boundaries = getParam<std::vector<BoundaryName>>("included_boundaries");
136  for (const auto & boundary_name : _boundary_names)
137  if (std::find(included_boundaries.begin(), included_boundaries.end(), boundary_name) !=
138  included_boundaries.end())
139  paramError(
140  "new_boundary",
141  "A boundary cannot be both the new boundary and be included in the list of included "
142  "boundaries. If you are trying to restrict an existing boundary, you must use a "
143  "different name for 'new_boundary', delete the old boundary, and then rename the "
144  "new boundary to the old boundary.");
145 
146  _included_boundary_ids = MooseMeshUtils::getBoundaryIDs(mesh, included_boundaries, false);
147 
148  // Check that the included boundary ids/names exist in the mesh
149  for (const auto i : index_range(_included_boundary_ids))
151  paramError("included_boundaries",
152  "The boundary '",
153  included_boundaries[i],
154  "' was not found within the mesh");
155  }
156 
158  {
159  const auto & excluded_boundaries = getParam<std::vector<BoundaryName>>("excluded_boundaries");
160  for (const auto & boundary_name : _boundary_names)
161  if (std::find(excluded_boundaries.begin(), excluded_boundaries.end(), boundary_name) !=
162  excluded_boundaries.end())
163  paramError(
164  "new_boundary",
165  "A boundary cannot be both the new boundary and be excluded in the list of excluded "
166  "boundaries.");
167  _excluded_boundary_ids = MooseMeshUtils::getBoundaryIDs(mesh, excluded_boundaries, false);
168 
169  // Check that the excluded boundary ids/names exist in the mesh
170  for (const auto i : index_range(_excluded_boundary_ids))
172  paramError("excluded_boundaries",
173  "The boundary '",
174  excluded_boundaries[i],
175  "' was not found within the mesh");
176 
178  {
179  // Check that included and excluded boundary lists do not overlap
180  for (const auto & boundary_id : _included_boundary_ids)
181  if (std::find(_excluded_boundary_ids.begin(), _excluded_boundary_ids.end(), boundary_id) !=
183  paramError("excluded_boundaries",
184  "'included_boundaries' and 'excluded_boundaries' lists should not overlap");
185  }
186  }
187 
188  // Get the boundary ids from the names
189  if (parameters().isParamValid("included_subdomains"))
190  {
191  // check that the subdomains exist in the mesh
192  const auto subdomains = getParam<std::vector<SubdomainName>>("included_subdomains");
193  for (const auto & name : subdomains)
195  paramError("included_subdomains", "The block '", name, "' was not found in the mesh");
196 
198  }
199 
200  if (parameters().isParamValid("included_neighbors"))
201  {
202  // check that the subdomains exist in the mesh
203  const auto subdomains = getParam<std::vector<SubdomainName>>("included_neighbors");
204  for (const auto & name : subdomains)
206  paramError("included_neighbors", "The block '", name, "' was not found in the mesh");
207 
209  }
210 
211  // We will want to Change the below code when we have more fine-grained control over advertising
212  // what we need and how we satisfy those needs. For now we know we need to have neighbors per
213  // #15823...and we do have an explicit `find_neighbors` call...but we don't have a
214  // `neighbors_found` API and it seems off to do:
215  //
216  // if (!mesh.is_prepared())
217  // mesh.find_neighbors()
218 }
219 
220 void
222 {
223  _qface.reset();
224  _fe_face.reset();
225 }
226 
227 void
229  const Point & normal,
230  const boundary_id_type & side_id,
231  MeshBase & mesh)
232 {
233  if (elem == nullptr || elem == remote_elem ||
234  (_visited[side_id].find(elem) != _visited[side_id].end()))
235  return;
236 
237  // Skip if element is not in specified subdomains
238  if (_check_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 unsigned int side,
275  const MeshBase & mesh) const
276 {
277  for (const auto & bid : _included_boundary_ids)
278  if (mesh.get_boundary_info().has_boundary_id(elem, side, bid))
279  return true;
280  return false;
281 }
282 
283 bool
285  const unsigned int side,
286  const MeshBase & mesh) const
287 {
288  for (const auto bid : _excluded_boundary_ids)
289  if (mesh.get_boundary_info().has_boundary_id(elem, side, bid))
290  return true;
291  return false;
292 }
293 
294 bool
296  const unsigned int side,
297  const MeshBase & mesh,
298  const Point & desired_normal,
299  const Point & face_normal)
300 {
301  // Skip if side has neighbor and we only want external sides
302  if ((elem->neighbor_ptr(side) && _include_only_external_sides))
303  return false;
304 
305  // Skip if side is not part of included boundaries
307  return false;
308  // Skip if side is part of excluded boundaries
310  return false;
311 
312  // Skip if element does not have neighbor in specified subdomains
314  {
315  const Elem * const neighbor = elem->neighbor_ptr(side);
316  // if the neighbor does not exist, then skip this face; we only add sidesets
317  // between existing elems if _check_neighbor_subdomains is true
320  return false;
321  }
322 
323  if (_using_normal &&
324  !MeshTraversingUtils::normalsWithinTol(desired_normal, face_normal, _normal_tol))
325  return false;
326 
327  return true;
328 }
bool elementSubdomainIdInList(const Elem *const elem, const std::vector< subdomain_id_type > &subdomain_id_list)
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:40
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:467
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:163
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.
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
auto norm(const T &a)
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:209
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)
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.
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